题目:
这道题一定要明白一点就是:每个点的出度为1,一点就只能形成一个环。也就是说从起点出发要么走回起点,要么就走进别人的环里无限循环,我们利用这一点就可以判断从一个起点出发是否能形成环,或者不能形成环走进别人的环里,如果从起点开始走,最终走回起点那么就是自己形成一个环,如果走到了一个已经走过的点那么就是走进别的环里了,无法成环。
所以直接爆搜每一个起点,判断其是否能形成环,如果能形成环那么就去判断是否更新答案。
代码:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int nextC[N]; //每个孩子最崇拜的孩子
bool visit[N];
int n, ans;
void dfs(int k, int step, int goal) // k是要去走哪个孩子,step是走了几步了,goal是终点也是起点
{
if (visit[k] && k != goal) return;
if (visit[k] && k == goal)
{
ans = max(ans, step);
return;
}
visit[k] = true;
dfs(nextC[k], step + 1, goal);
visit[k] = false;
}
int main()
{
cin>>n;
for(int i = 1; i <= n; ++i) cin >> nextC[i];
for(int i = 1; i <= n;++i)
{
visit[i] = true;
dfs(nextC[i], 1, i);
}
cout<<ans;
return 0;
}
时间复杂度是n2的,但是官网确实是能过全部数据的
从中还是可以看出有很多重复了,从起点开始走要么走进自己的环里要么走进别人的环里,从中可以看出都可以成环(我们只需要计算这个过程中的环的大小)。并且每一个点只要走过一次那么他就没有再走的必要了,因为只要走过一次那么这个点的性质就已经确定了(大家自己悟哈,我表达不出来),每个点只经过一次时间复杂度O(n),标记下来每个点是第几个经过的,那么一遇到重复的点时就是遇到圈了,这个圈的大小就是上一个的标号减去这个重复经历点的标号在加一。但是这个计算方式只有在所有的点都是第一次经过时才生效,如果说最后经历的那个重复的点的编号大于起点标号那就是第一次经过,如果经历的标号小于起点的编号那么就代表他已经走过了有关的性质已经确定了,没有在往下进行下去的必要了。
例如:
代码:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int nextC[N]; //每个孩子最崇拜的孩子
int dfn[N]; //每个点的时间戳
int n, ans, idx;
int dfs(int u, int start)
{
dfn[u] = ++idx;
if (dfn[nextC[u]] && dfn[nextC[u]] >= start) //有可能时自环所以时>=
return dfn[u] - dfn[nextC[u]] + 1;
if (dfn[nextC[u]] && dfn[nextC[u]] < start) return 0;
return dfs(nextC[u], start);
}
int main()
{
cin>>n;
for(int i = 1; i <= n; ++i) cin >> nextC[i];
for(int i = 1; i <= n;++i)
{
if (!dfn[i])
ans = max(ans, dfs(i, idx + 1));
}
cout<<ans;
return 0;
}