图的遍历
题目描述
给出 N 个点, M条边的有向图,对于每个点 v ,求 A(v) 表示从点 v 出发,能到达的编号最大的点。
输入输出格式
输入格式:
第1 行,2 个整数 N,M 。
接下来 M行,每行2个整数 Ui,Vi ,表示边 (Ui,Vi) 。点用 1,2,⋯,N 编号。
输出格式:
N 个整数 A(1),A(2),⋯,A(N) 。
输入输出样例
说明
• 对于60% 的数据,1≤N.K≤103 ;
• 对于100% 的数据,1≤N,M≤105 。
分析:
一开始看到这题想到的还是Tarjan缩点+dfs,但是还有更加巧妙的方法可以更简便地AC。
首先分析,直接搜索肯定是有错误的,因为会有环,如果数据故意卡的话是很容易卡死的(因为遍历顺序是与存边顺序有关的,而且又是有向图,直接搜可能会导致WA掉)
当然环可以缩点去掉。但是这样太麻烦了,不如换个思路。
因为要求的是可以到达的编号最大的点,那么我们可以反向建边,问题就转换为求可以到达该点的最大编号的点,那么就从n到1反向遍历,直接一边dfs即可。当然要注意,该图不一定是个连通图。具体看代码。
Code:
#include<bits/stdc++.h> using namespace std; const int N=1e5+7; int n,m,head[N],size,ans[N]; struct Node{int to,next;}edge[N]; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline void add(int x,int y) { edge[++size].to=y; edge[size].next=head[x]; head[x]=size; } inline void dfs(int u,int sum) { if(ans[u])return; ans[u]=sum; for(int i=head[u];i!=-1;i=edge[i].next){ dfs(edge[i].to,sum);} } int main() { n=read();m=read();int x,y; memset(head,-1,sizeof(head)); memset(ans,0,sizeof(ans)); for(int i=1;i<=m;i++){ x=read();y=read();add(y,x);} for(int i=n;i>=1;i--) if(!ans[i])dfs(i,i); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }