图的遍历
【题目描述】给出 个点, 条边的有向图,对于每个点 ,求 表示从点 出发,能到达的编号最大的点。 【输入】第 行 个整数 ,表示点数和边数。 接下来 行,每行 个整数 ,表示边 。点用 编号。 【输出】一行 个整数 。 【输入样例】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; }