图的遍历
【题目描述】给出 个点, 条边的有向图,对于每个点 ,求 表示从点 出发,能到达的编号最大的点。 【输入】第 行 个整数 ,表示点数和边数。 接下来 行,每行 个整数 ,表示边 。点用 编号。 【输出】一行 个整数 。 【输入样例】4 3 1 2 2 4 4 3 【输出样例】4 4 3 4 【提示】对于 的数据,。 对于 的数据,。 |
【题目分析】
题目要求对于每个点v,找出从它出发能到达的编号最大的点。
也就是说,如果从v出发,出发可以到达点x1,x2,x3.... 那么
R(v)=max(x1,x2,x3.....)
其中也包括v 本身(因为从v 出发至少能到达自己)。
一个常见的思路是
反向图 + 按编号从大到小 BFS
一个常见的技巧是:
建反向图(即原图中的边 u→v 变成 v→u)。
从编号最大的点 开始 BFS,在反向图中能到达的点 ,意味着在原图中 能到达 。
那么这些点的答案至少是 。
然后从 开始,如果它还没被访问过,就 BFS,能到达的点答案至少是 ,但注意如果之前已经赋过更大的值,就不用更新。
这样按编号从大到小做 BFS,每个点只会被访问一次,因为一旦被访问,它的答案就已经确定(是当前起点的编号,并且这个编号是递减考虑的,所以第一次访问时就是最大可能值)。
算法步骤:
构建反向图
rG。初始化答案数组
ans[i] = 0。从
i = N到1:如果
i还没有被访问过,从i开始在反向图上 BFS/DFS。在 BFS 过程中,对于每个到达的点
u,如果它还没被访问过,就设置ans[u] = i,并标记访问,加入队列。输出
ans[1..N]。
【参考答案】
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
int N, M;
cin >> N >> M;
vector<vector<int>> rG(N + 1);
for (int i = 0; i < M; i++) {
int u, v;
cin >> u >> v;
rG[v].push_back(u);
}
vector<int> ans(N + 1, 0);
vector<bool> visited(N + 1, false);
for (int i = N; i >= 1; i--) {
if (visited[i]) continue;
queue<int> q;
q.push(i);
visited[i] = true;
ans[i] = i;
while (!q.empty()) {
int cur = q.front();
q.pop();
for (int j = 0; j < rG[cur].size(); j++) {
int nxt = rG[cur][j];
if (!visited[nxt]) {
visited[nxt] = true;
ans[nxt] = i;
q.push(nxt);
}
}
}
}
for (int i = 1; i <= N; i++) {
cout << ans[i];
if (i < N) {
cout << " ";
}
}
cout << endl;
return 0;
}扫描二维码推送至手机访问。
版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。


