1. 问题描述:
班里 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] 构成的圈。
运行限制
最大运行时间:1s
最大运行内存: 256M
来源:https://www.lanqiao.cn/problems/182/learning/
2. 思路分析:
分析题目可以知道我们可以搜索所有可能的圈(也可以使用模拟的思路进行求解),从当前起点i出发开始搜索,因为存在圈也就是存在环,所以在dfs搜索全部可能的圈的时候需要标记一下哪些位置是已经访问过的,并且在dfs方法中需要传递一开始的起点位置这样这样我们可以在递归过程中可以判断当前递归的位置是否等于了起始位置,如果相等说明就存在环了,因为还需要计算圈的数目,所以需要在递归的过程中维护一个变量,我们可以在递归方法中传递一个int类型的变量来记录圈的数目,但是不知道为什么在c语言网站上提交代码只是通过了75%的测试用例,不知道还有哪些情况是没有考虑到的
3. 代码如下:
from typing import List
class Solution:
res = 1
def solve(self, N: int, vis: List[int], nums: List[int]):
for i in range(len(nums)):
# 从当前的起点出发搜索可能的圈
self.dfs(N, vis, nums, i, i, 0)
return self.res
def dfs(self, N: int, vis: List[int], nums: List[int], start: int, cur: int, count: int):
if vis[cur] and cur == start:
# 存在一个圈
self.res = max(self.res, count)
return
# 访问过了那么就直接返回了
if vis[cur]: return
vis[cur] = 1
# 因为递归的时候是下表而在数组中对应的是从1开始的编号所以减1才是正确的
self.dfs(N, vis, nums, start, nums[cur] - 1, count + 1)
vis[cur] = 0
if __name__ == '__main__':
# dfs搜索其实也可以使用模拟的方法解决, 看懂题目中的那个图就可以很好解决了
N = int(input())
nums = list(map(int, input().split()))
# 标记数组用来标记递归过程中访问过的位置
vis = [0] * N
print(Solution().solve(N, vis, nums))
一个类似的思路但是使用c++通过了的代码:
#include <iostream>
#include<cstring>
#include<math.h>
using namespace std;
int stuMap[100000];
int MAX = 0;
int vis[100000];
int cnt = 0;
void dfs(int i, int ori)
{
if(vis[i] && i!=ori)
return;
if(vis[i] && i==ori)
{
MAX = fmax(MAX, cnt);
return;
}
++cnt;
vis[i] = 1;
dfs(stuMap[i], ori);
vis[i] = 0;
--cnt;
}
int main()
{
int N;
cin >> N;
for (int i = 1; i <= N; ++i)
cin >> stuMap[i];
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= N;++i)
dfs(i,i);
cout << MAX << endl;
return 0;
}