题目描述
班里 N 个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,⋯N。
输入描述
输入第一行,一个整数 N(3<N<10^5)。
接下来一行 N个整数,由空格分开。
输出描述
要求输出一个整数,表示满足条件的最大圈的人数。
样例">输入输出样例
示例
输入
9
3 4 2 5 3 8 4 6 9
输出
4
样例解释
如下图所示,崇拜关系用箭头表示,红色表示不在圈中。
显然,最大圈是[2 4 5 3] 构成的圈。
分析:
题目会拿到一个圈,我们可以用数组来存放圈,数组长度是N+1,这样我们就可以从下标1开始,到N,下标1就是第一个学生,这个位置存放的是第一个学生指向的下一个学生,也就是样例中的,第一个学生指向第三个学生,所以array[1] = 3;然后第二个学生也是类似。
我们采用dfs来处理,首先我们从学生1开始,依次往下遍历他指向的下一个学生,他指向的下一个学生的下一个学生这样。但是题目给出的数据里面是存在环的(也就是不加结束条件的话会成为死循环),那么我们就需要处理环。
处理环的话我考虑使用一个和学生数组一样长度的boolean数组,boolean数组的值只能是true和false,我们初始化boolean数组为false,当我们进入循环,就把当前下标对应的boolean数组的值修改为true,我们进入循环的判断就是判断当前下标的boolean数组是否为false,如果是false,代表这个下标还没有遍历过,可以继续进行遍历,如果遍历过了,那么就不再进行循环。
遍历完成一遍之后,肯定会结束的(环的特性),我们只需要判断初始下标进的循环,出来循环的下标,这两个下标是否相同,如果相同,他们就完成了一次闭环,即这个下标可以构成一个环。如果不相同,那么就是里面存在环,但这个环不是从初始下标开始的。就继续往下找环的初始节点。
因为不知道提供的数据里面有几个环,我们每找到一个环,都对最大圈数进行一次判断和更新。
Java:
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
//记录环的最大圈数
static int max = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//拿数据
int n = scan.nextInt();
//小朋友指向的朋友就放到i的下面,即array[1] = 3 ,第一个朋友的旁边是3
int[] array = new int[n+1];
//第一个小朋友就放到第i个位置上
for(int i = 1;i<=n;i++){
array[i] = scan.nextInt();
}
scan.close();
//循环每一个学生,通过dfs找到他们环,记录和更新在max
for(int i = 1;i<=n;i++){
dfs(array,i);
}
System.out.println(max);
}
public static void dfs(int[] array,int i){
//创建一个boolean数组,记录和array相同的位置有没有被访问,如果没有被访问,那么就通过该下标的指向继续遍历
boolean[] count = new boolean[array.length];
//进入循环的初始节点
int res = i;
//环的圈数
int sum = 0;
//初始化的count是用false填充的
while(count[res]==false){
//进来之后将当前位置改为true,代表这个节点已经进来过,防止死循环
count[res] = true;
//当前环的圈数+1
sum++;
//前往这个学生指向的下一个学生
res = array[res];
}
//从环出来后,res就是环的初始节点,判断是否和我们传递进来的i相同,
//如果相同,代表这个学生能够形成一个环,然后更新max即可
if(res==i){
max = Math.max(sum,max);
}
}
}