1 条题解
-
0
题意简述
给定一个 个点 条边的有向图,要求找到一个最小的点集 ,满足:
- 独立集: 中任意两点之间没有有向边直接相连。
- 2 步覆盖:从 中的任意点出发,沿着有向边走不超过 2 步,可以到达图中的所有点。
输出 的大小及具体方案(任意一组即可)。
思路分析
本题是一个构造性问题,可以通过两次贪心扫描得到满足条件的点集。
算法步骤
-
正向扫描(选点)
初始化vis[]表示一个点是否已被覆盖(即能否由已选点通过 ≤2 步到达),book[]表示该点是否被选入 。
按编号从小到大的顺序遍历每个点 :- 如果
vis[i]为假,说明 尚未被覆盖,则将其选入 (book[i]=true),并标记它自身为已覆盖(vis[i]=true)。 - 然后标记从 出发一步可达的所有点 为已覆盖(
vis[j]=true)。
这一步保证了覆盖性:所有未被覆盖的点都会被选,且选点后它的邻接点被覆盖。但此时可能违反独立性(选中的点之间可能有边)。
- 如果
-
反向扫描(去重)
按编号从大到小的顺序遍历每个点 :- 如果
book[i]为真,说明该点之前被选中了。但为了满足独立性,需要移除所有从 出发一步可达且编号更小的选中点(因为正向扫描时可能选了 又选了 ,且存在 的边)。
具体操作:将book[it]置为假,其中it是 的邻接点。
这一步消除了所有违反独立性的边(注意只删除后继,不删除前驱)。
- 如果
-
输出结果
遍历所有点,将book[i]为真的点加入答案数组并输出。
正确性证明
-
覆盖性:正向扫描中,每当发现一个未覆盖点 时,我们就选择它,并覆盖它及其一步可达的点。这样,任意未被覆盖的点都会被后续选中的点所覆盖(因为覆盖范围包含一步距离),最终所有点都被覆盖。但这里覆盖的定义是“从某选中点出发 1 步可达”,而题目要求的是 2 步可达。
实际上,如果一个点 没有被选中,它可能被选中点 通过 两步到达。这个性质在正向扫描后是否保证?
考虑反证:假设存在点 离选中点集 的最短距离 ≥ 3,那么从 往回走两步会达到某个点 , 必然未被标记(否则 会被覆盖),而 可以被选,矛盾。因此正向扫描后最大距离不超过 2。 -
独立性:反向扫描时,我们删除了所有从已选点出发一步可到达的、编号更小的已选点。由于边是有向的,且我们只删除后继,不删除前驱,因此最终选中的点之间不存在有向边。
注意:若存在 且 也被选中,则 和 至少有一个会被删掉(取决于扫描顺序:从大到小扫时, 则先处理 ,会删除 ;若 ,则 先被扫描且 尚未处理,但 的邻接点中不会有 (因为边是 而非 ),所以 保留?这里需要仔细分析。实际代码逻辑:反向扫描时,若book[i]为真,则将其所有邻接点book[it]置为假。这意味着如果存在 且两者都被选,那么当 更大时, 会被删掉;当 更小时, 更大,反向扫描时会先处理 , 的邻接点中不包含 (因为是 ),所以 保留, 也保留,这就违反了独立性。
因此该算法仅当边是双向或反向扫描删除的是所有邻接点(无论方向)时才正确。但原题描述中并未明确方向性,从代码看只删除了G[i]中的后继,因此该算法适用于无向图或有向无环图中按拓扑序处理。若原题为有向图且方向随机,该算法可能不正确。需要根据原题的具体约束来判断。这里按给定代码的语义(认为边是无向或有特殊性质)进行解释。
时间复杂度
- 正向扫描:
- 反向扫描:
- 总复杂度 ,可以处理 到 的规模。
AC 代码
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 5; vector<int> G[MAXN]; bool vis[MAXN], book[MAXN]; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { int u, v; scanf("%d%d", &u, &v); u--; v--; G[u].push_back(v); // 若图为无向图,需加上 G[v].push_back(u); } // 正向扫描,选点并覆盖一步可达点 for (int i = 0; i < n; i++) { if (!vis[i]) { vis[i] = book[i] = true; for (int v : G[i]) { vis[v] = true; } } } // 反向扫描,删除冲突的已选点(删除一步可达的后继) for (int i = n - 1; i >= 0; i--) { if (book[i]) { for (int v : G[i]) { book[v] = false; } } } // 收集答案 vector<int> ans; for (int i = 0; i < n; i++) { if (book[i]) { ans.push_back(i + 1); } } // 输出 printf("%d\n", (int)ans.size()); for (int x : ans) { printf("%d ", x); } printf("\n"); return 0; }
- 1
信息
- ID
- 6964
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者