看了很多这道题的题解,大多数都是反向建图和tarjan的做法。但是我觉得tarjan对于初学者来说有点复杂了。所以我就写了一篇正向建图的dfs做法。
题目链接:P3916 图的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
代码上有详细的注释
#include <bits/stdc++.h>
#define N 100010
using namespace std;
struct node {
int t,nex;
}rt[N];
int head[N],cnt;
int maxx[N];
int a,b;
int vis[N];
void add(int x,int y) {
cnt ++;
rt[cnt].t = y;
rt[cnt].nex = head[x];
head[x] = cnt;
}
int dfs(int x,int f) { // 整个dfs的做法很简单用vis来标记是否去过,去过就不去了。如果去过就更新一下当前这个点的值。返回maxx。
for(int i = head[x];i;i = rt[i].nex) {
if(!vis[rt[i].t]) {
vis[rt[i].t] = 1;
maxx[x] = max(dfs(rt[i].t,x),maxx[x]);
}
else {
maxx[x] = max(maxx[x],maxx[rt[i].t]);
}
}
return maxx[x];
}
int main() {
cin>>a>>b;
int x,y;
for(int i = 1;i <= b;i ++) {
scanf("%d%d",&x,&y);
add(x,y); // 正常建图
}
for(int i = 1;i <= a;i ++) {
maxx[i] = i; //预处理每个点的最大值就是自己
}
for(int i = 1;i <= a;i ++) {
if(!vis[i]) {
vis[i] = 1;
dfs(i,0); // 这里需要进行第一次的dfs 从每个没找过的点开始。
}
}
memset(vis,0, sizeof(vis));
for(int i = 1;i <= a;i ++) {
if(!vis[i]) {
vis[i] = 1; //这里第二次的dfs其实我也想了很久才想出来,其实原理很简单就是在我们第一次的dfs更新答案的时候存在
//某一个点在更新的时候这个的走到了一个去过的点(可以简单的理解为从一个点开始走了一个环又走回了自己,但是这个环的起点还没有更新其他的点所以只更新一次的话就会造成更新不到,某些点。)
dfs(i,0);
}
}
memset(vis,0, sizeof(vis));
for(int i = 1;i <= a;i ++) {
if(!vis[i]) {
vis[i] = 1; //第三次dfs的原理同上,第二次dfs后会更新很多点的值,可能有些点的序号在前面就没有更新到所以需要进行第三次dfs。
dfs(i,0);
}
}
for(int i = 1;i <= a;i ++) {
cout<<maxx[i]<<" ";
}
return 0;
}