Description
有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
Input
输入共2行。
第1行包含1个正整数n表示n个人。
第2行包含n个用空格隔开的正整数T1,T2,……,Tn其中第i个整数Ti示编号为i
的同学的信息传递对象是编号为Ti的同学,Ti≤n且Ti≠i
数据保证游戏一定会结束。
Output
输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。
Sample Input
5
2 4 2 3 1
Sample Output
3
Hint
游戏的流程如图所示。当进行完第 3 轮游戏后, 4 号玩家会听到 2 号玩家告诉他自
己的生日,所以答案为 3。当然,第 3 轮游戏后, 2 号玩家、 3 号玩家都能从自己的消息
来源得知自己的生日,同样符合游戏结束的条件。
对于 30%的数据, n ≤ 200;
对于 60%的数据, n ≤ 2500;
对于 100%的数据, n ≤ 200000。
题解
抓取有用信息:
1、有向图;
2、每个点只有一条出边。
100分算法:
1、有向图很容易想到$Floyd$;
2、每个点只有一条出边意味着边数是$O(n)$的,而且每一个环都没有出边;大家可以各种画图感受一下这张图的性质;
3、最后得到的结论就是图的中间有若干个环,周围的点各种连向环;
4、我们的目的是找出最小的环,那么我们只要把每个环旁边的支链去掉,然后求环的长度就行了;
5、去掉支链可以用$BFS$;
6、为什么是$BFS$?因为除了环以外的部分都有鲜明的层次性;
7、具体做法:从没有入边的点开始搜,不断地删点,然后更新剩下的还有哪些点没有入边;删点删到剩下的都是有入边的点,就是一堆环了;
8、然后$DFS$对每个环求环长,结果取最小值即为答案。
也可以用强连通分量做。
另外大家可以去学一个叫基环内向树的东西。
1 #include<set> 2 #include<map> 3 #include<ctime> 4 #include<cmath> 5 #include<queue> 6 #include<stack> 7 #include<vector> 8 #include<cstdio> 9 #include<string> 10 #include<cstring> 11 #include<cstdlib> 12 #include<iostream> 13 #include<algorithm> 14 #define LL long long 15 #define Max(a,b) ((a)>(b) ? (a):(b)) 16 #define Min(a,b) ((a)<(b) ? (a):(b)) 17 using namespace std; 18 const int N=200000; 19 const int INF=~0u>>1; 20 21 int n; 22 int to[N+5]; 23 int in[N+5]; 24 queue<int>Q; 25 26 int main() 27 { 28 scanf("%d",&n); 29 for (int i=1;i<=n;i++) scanf("%d",&to[i]),in[to[i]]++; 30 for (int i=1;i<=n;i++) 31 if (!in[i]) Q.push(i); 32 while (!Q.empty()) 33 { 34 int u=Q.front();Q.pop(); 35 in[to[u]]--; 36 if (!in[to[u]]) Q.push(to[u]); 37 } 38 int ans=INF; 39 for (int i=1;i<=n;i++) 40 if (in[i]) 41 { 42 in[i]=0; 43 int cnt=1; 44 for (int j=to[i];j!=i;j=to[j]) cnt++,in[j]=0; 45 ans=Min(ans,cnt); 46 } 47 printf("%d ",ans); 48 return 0; 49 }