题目描述
给出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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}