题目描述
有 n 个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入输出样例
输入
5
2 4 2 3 1
输出
3
题意很简单,如果信息可以传递回来,代表形成了一个环,所以题意就是要求找一个最小环。
看到这道题,有几种想法,可以用并查集找最小环,一直更新即可。另一种就是用 tarjan 找强连通分量,形成环,肯定构成一个强连通,那么直接 tarjan 找强连通大于 1 ,的最小强连通分量即可。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,dfn[N],low[N],vis[N],cnt,res=0x3f3f3f3f;
int head[N],nex[N],to[N],tot;
stack<int> st;
inline void add(int a,int b){
to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt; st.push(x); vis[x]=1;
for(int i=head[x];i;i=nex[i]){
if(!dfn[to[i]]){
tarjan(to[i]); low[x]=min(low[x],low[to[i]]);
}
else if(vis[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(low[x]==dfn[x]){
int ans=0;
while(1){
ans++; int u=st.top(); st.pop(); vis[u]=0;
if(u==x) break;
}
if(ans>1) res=min(res,ans);
}
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int x; cin>>x; add(i,x);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
cout<<res<<endl;
return 0;
}