[Noip2015] 信息传递
有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号
为i的同学的信息传递对象是编号为Ti同学。游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同
时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只
会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一
共可以进行几轮? 简单的说就是给你一个有向图,让你找出一个最小的环来。
输入
输入共2行。
第1行包含1个正整数n表示n个人。 n ≤ 200000
第2行包含n个用空格隔开的正整数T1,T2,……,Tn
其中第i个整数Ti示编号为i的同学的信息传递对象是编号为Ti的同学,Ti≤n且Ti≠i
数据保证游戏一定会结束。
输出
输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。
样例
输入
复制
5 2 4 2 3 1
输出
复制
3
Sol1:topsort
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn=200000+10; int to[maxn],into[maxn],n,q[maxn],ans=1e7; bool vis[maxn]; void bfs() { int head=0,tail=0; for(int i=1; i<=n; i++) if(!into[i]) //将入度为0的点进队列 { vis[i]=1; tail++; q[tail]=i; } head=1; while(head<=tail) { into[to[q[head]]]--; if(!into[to[q[head]]]) { tail++; q[tail]=to[q[head]]; vis[q[tail]]=1; } head++; } } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&to[i]); into[to[i]]++; } bfs(); for(int i=1; i<=n; i++) { int cnt=0; if(!vis[i]) { int k=i; while(!vis[k]) { vis[k]=1; k=to[k]; cnt++; } ans=min(cnt,ans); } } cout<<ans; return 0; }
并查集
#include <iostream> #include <algorithm> #include <cstring> #include <map> #include <cstdio> #include <cmath> #include <vector> #include <queue> using namespace std; const int inf=1e9; const int N=200020; int n,m,step,ans; int d[N],num[2010],e[N],ne[N],idx,w[N],st[2010],h[N],f[N]; int a[N],vis[N],cnt[N]; typedef pair<int ,int > PII; /*并查集做法*/ int find(int x) { if(x!=f[x]) { int z=f[x]; f[x]=find(f[x]); d[x]=d[x]+d[z]; } return f[x]; } void Union(int x,int y) { int fa=find(x); int fy=find(y); if(fa==fy) ans=min(ans,d[x]+d[y]+1); //有相同父节点说明本来就在同一条链上, //再加上他们之间的边所以形成环,更新最小环 else { f[fa]=fy; d[x]=d[y]+1; } } int main() { cin>>n; ans=inf; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); Union(i,a[i]); } cout<<ans<<endl; return 0; }