图(基环树)
链接:https://ac.nowcoder.com/acm/contest/3007/B
来源:牛客网
题目描述
现在有一个N个点的有向图,每个点仅有一条出边
你需要求出图中最长的简单路径包含点的数量
(1≤N≤1,000,000)
输入描述:
第一行一个数字N
接下来N行,每行一个正整数,第i+1行的数字表示第i个点出边终点的编号
(点从1开始标号)
输出描述:
一行一个数字,最长的简单路径的长度
输入
3 2 3 2
输出
3
官方题解:
这是一个简单的有关基环树的问题。
可以证明,每个点出度都为1的有向图是一个基环内向树森林。
关于这一部分的内容,可以自行查阅资料。
这里我们要用到的结论是:从一个点出发,沿着出边一路走下去,一定会走到一个环。
所以我们选择dfs,当遍历到一个已在dfs栈中的节点时,就说明找到了环,可以结束统计。
但这样是会超时的,于是我们选择带“记忆化”的dfs,从一个点开始沿着出边走下去,每当走到一个在之前某次dfs中经过的点时,就可以利用上一次的答案完成求解。
实现上有一些细节,要注意不要让复杂度退化。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const double eps =1e-8; 16 const int mod=1e9+7; 17 const int maxn=1e6+10; 18 using namespace std; 19 20 int to[maxn]; 21 int dis[maxn]; 22 int vis[maxn]; 23 24 int DFS(int u) 25 { 26 vis[u]=1;//入栈 27 if(dis[u])//已经计算过 28 { 29 vis[u]=0;//出栈 30 return dis[u]; 31 } 32 if(to[u]==0)//该点没有出边,好像没什么用 33 { 34 vis[u]=0; 35 return dis[u]=1; 36 } 37 if(vis[to[u]])//遇到环 38 { 39 int rt=to[u];//环开始的地方 40 int v=to[rt]; 41 int cnt=1;//计数 42 while(v!=rt)//数一下这个环有几个点 43 { 44 cnt++; 45 v=to[v]; 46 } 47 dis[rt]=cnt; 48 v=to[rt]; 49 while(v!=rt)//更新该环内所有的dis 50 { 51 dis[v]=cnt; 52 v=to[v]; 53 } 54 vis[u]=0; 55 return dis[u]; 56 } 57 int res=DFS(to[u])+1;//递归; 58 vis[u]=0;//出栈 59 if(dis[u]) return dis[u];//环内计算过了,记忆化搜索 60 else return dis[u]=res;//否则正常 61 } 62 63 int main() 64 { 65 #ifdef DEBUG 66 freopen("sample.txt","r",stdin); 67 #endif 68 69 int n,m; 70 scanf("%d",&n); 71 for(int i=1;i<=n;i++) 72 scanf("%d",&to[i]); 73 int ans=0; 74 for(int i=1;i<=n;i++) 75 { 76 if(!dis[i]) DFS(i); 77 ans=max(ans,dis[i]);//求出图中最长的简单路径包含点的数量 78 } 79 printf("%d\n",ans); 80 81 return 0; 82 }
-