题目描述
有 nn 个同学(编号为 11 到 nn )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 ii的同学的信息传递对象是编号为 T_iTi 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入输出格式
输入格式:输入共2行。 第1行包含1个正整数 nn ,表示 nn 个人。
第2行包含 nn 个用空格隔开的正整数 T_1,T_2,\cdots\cdots,T_nT1,T2,⋯⋯,Tn ,其中第 ii 个整数 T_iTi 表示编号为 ii 的同学的信息传递对象是编号为 T_iTi 的同学, T_i \leq nTi≤n 且 T_i \neq iTi≠i 。
输出共1行,包含1个整数,表示游戏一共可以进行多少轮。
解析:把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。
图论求最小环,我在里面看到了并查集。
假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。
如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。
代码如下:
import java.util.Scanner;
public class huan {
//并查集的最小环
static int f[] = new int[200002]; //保存祖先节点
static int d[] = new int[200002]; //保存其祖先节点路径长
static int n,minn,last;
public static int jion(int x) {
if(f[x]!=x) { //查找是沿途更新祖先节点和路径长
last = f[x]; //记录父节点,会在递归中被更新
f[x] = jion(f[x]);//更新父节点
d[x]+=d[last];//更新路径长
}
return f[x];
}
public static void check(int a,int b) {
int x = jion(a),y = jion(b); //查找祖先节点
if(x!=y) {
f[x]=y; //若不相连,则连接两点,更新父节点和路径长
d[a]=d[b]+1;
}else {
minn = Math.min(minn, d[a]+d[b]+1);
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
for(int i=1;i<=n;i++) {
f[i] =i;
}
minn = Integer.MAX_VALUE;
for(int i =1;i<=n;i++) {
int t = input.nextInt();
check(i,t);
}
System.out.println(minn);
}
}