2020牛客寒假算法基础集训营6 B题(基环树)

2020牛客寒假算法基础集训营6 B题

B-图

传送门

这是最后一场基础训练啦~看过题解的我居然真的有这种情况=-=
QAQ
我是真的有些细节没有想清楚,提交了,竟然过了???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;
}

下次补补树的直径的博客~

发布了53 篇原创文章 · 获赞 2 · 访问量 1370

猜你喜欢

转载自blog.csdn.net/qq_44624316/article/details/104343692