2020牛客寒假算法基础集训营6 B题
B-图
这是最后一场基础训练啦~看过题解的我居然真的有这种情况=-=
我是真的有些细节没有想清楚,提交了,竟然过了???emmm
我惊了=-=
这个题目时一个关于基环树的题目啦,记忆化搜索可以解决(不然会超时qaq)
当初比赛一看到这个题目的时候脑海里瞬间想的是两次dfs求树的直径(该打,不看清题目就写的痛苦太难受了)
后来还是出题人在广播里左强调,右强调,我才=-=又仔细看了好几遍题目,题目给的是有向图,可以有自环(我还傻乎乎的求树的直径,四不四傻!)
题目要求输出最长的简单路径上面的结点个数=-=
每个结点的出度均为1(这里我们可以得到这是基环内向树,如果每个结点点的入度为1的话,那就是基环外向树),我们可以先处理环的部分,先处理每个环有多少个结点=-=
基环树有一些这样的特征:
构成环的每个结点都是树根噢,它下面都连着一颗小树~
沿着一个结点走下去,一定可以走到环~
所以我们的解题思路就是~
首先处理环部分,把构成环部分的结点个数算出来,然后dfs记忆化搜索最长的路径就可以啦~
那么问题来啦,我们怎么知道哪些是环部分呢,这就需要用到上面的结论啦,就是沿着一个结点走下去,一定可以走到环的~
在这里解释一下开的数组的含义:
int a[N];//记录出度的结点
int vis[N];//记录是否访问过
int s[N];//小tips,s[0]记录当前访问的结点个数,之后的s[1],s[2]…记录访问结点的顺序,储存结点的序号
int ins[N];//记录当前环的结点是否访问过,与vis[]用法记录相反
int siz[N];//预处理部分记录环的大小,dfs部分记录最长路径的结点个数~
dfs记忆化搜索有了前面的处理部分,这部分就很好写啦~~~
上代码啦~
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n;
int a[N];
int vis[N];
int s[N];
int ins[N];
int siz[N];
int dfs(int x)
{
if (siz[x])
{
return siz[x];
}
return siz[x] = 1 + dfs(a[x]);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf ("%d", &a[i]);
}
int i, j, k, h;
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
j = i;
while (!vis[j])
{
s[++s[0]] = j;
ins[j] = vis[j] = 1;
j = a[j];
}
if (ins[j])
{
k = j;
h = 0;
do
{
k = a[k];
h++;
}while (k != j);
do
{
k = a[k];
siz[k] = h;
}while (k != j);
}
while (s[0])
{
ins[s[s[0]]] = 0;
--s[0];
}
}
}
for (i = 1, h = 0; i <= n; i++)
{
h = max(h, dfs(i));
}
cout << h << endl;
return 0;
}
下次补补树的直径的博客~