洛谷P3916 图的遍历
题目描述
给出N个点,M条边的有向图,对于每个点v,求A(v)表示从点v出发,能到达的编号最大的点。
输入格式
第1 行,2 个整数N,M。
接下来M行,每行2个整数
U
i
,
V
i
U_i,V_i
Ui,Vi,表示边
(
U
i
,
V
i
)
(U_i,V_i)
(Ui,Vi)。点用
1
,
2
,
⋯
,
N
1, 2,\cdots,N
1,2,⋯,N编号。
输出格式
N 个整数
A
(
1
)
,
A
(
2
)
,
⋯
,
A
(
N
)
A(1),A(2),\cdots,A(N)
A(1),A(2),⋯,A(N)。
输入#1:
4 3
1 2
2 4
4 3
输出#1:
4 4 3 4
• 对于100% 的数据, 1 ≤ N , M ≤ 1 0 5 1 \le N , M \le 10^5 1≤N,M≤105。
最原始的思路里,穷举每一个开始节点然后dfs寻找可能达到的最大的点,这样的复杂度显然是 O ( n 2 ) O(n^2) O(n2)的,估计是会超时的,想想能不能优化?
我们不如反过来思考,从大到小遍历每个点,看每个点会从哪些点到达。比如说i点被j点到达,那么之后就无需再考虑j点了,因为j点至多到达的点就是i。所以我们采取反向建图的方式:
#include <iostream>
#include <limits.h>
#include <cstdio>
#include <cmath>
#include <stack>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
#include <queue>
#include <cstring>
#include <fstream>
#include <map>
#include <list>
#include <set>
using namespace std;
int n,m;
vector<int> g[100010]; //反向建图
int res[100010]; //res[i]表示i能达到的最大点,最开始设置为0表示未访问过
void dfs(int now,int from){ //now表示遍历的点,from表示是尽头是from,即可以从now到达from
res[now]=from;
for(int k=0;k<g[now].size();k++){
int to=g[now][k];
if(!res[to]){
dfs(to,from);
}
}
}
int main(){
cin>>n>>m;
while(m--){
int u,v;
scanf("%d %d",&u,&v);
g[v].push_back(u);
}
memset(res,0,sizeof(res));
for(int i=n;i>=1;i--){
if(!res[i]){
res[i]=i;
dfs(i,i);
}
}
for(int i=1;i<=n;i++) printf("%d ",res[i]);
return 0;
}