树的重心 题型:图论模板题

130 阅读2分钟

846. 树的重心 - AcWing题库

解析

题意是说依次删除一个点后存一下剩余的连通块的最大值,在这些最大值中求一个最小值。

题意模拟

样例图如下所示:

image.png

我们先删除根节点,此时有3个连通块,其中连通块中节点个数最多的是以4为根节点的子树的连通块,节点个数是4,所有根节点1的权重就是4:

image.png

接下来我们删除节点2,此时还剩两个连通块,其中连通块中节点个数最多的是以节点1为根节点的连通块,节点个数为6,所以节点2的权重就是6。

image.png

就这样枚举下去,然后求每个节点中权重最小的那个。

思路

问题就在于怎么求一个连通块内的个数。

我们知道数是向下遍历的,假设我们在节点4这个位置,我们只需要算上节点4本身这个节点个数,然后向下遍历节点4的子树,统计子树中节点的个数,一直遍历到空为止,此时向上返回统计的节点的个数,这个时候节点4存的就是以节点4为根的这个连通块中的节点的个数了,我们用size4表示:

image.png

此时,我们知道了以节点4为根节点的这个连通块的数量,那么不包含节点4的连通块中节点个数就等于n-size4:

image.png

code

#include<bits/stdc++.h>
using namespace  std;
const int N = 1e5 + 10, M = N * 2;
int  n;


int h[N]; //头节点
int e[M]; //边值
int ne[M]; //next指针
int idx;   //地址
bool st[N];

int ans = N;  //ans存所有最大值中的最小值


//在a这个节点插入b这个边
void add(int a,int b)
{
	e[idx] = b;      //把b赋值给当前边值
	ne[idx] = h[a];  //指向下一个节点
	h[a] = idx++;    //头指针移动
}



int dfs(int u)
{
	st[u] = true; //标记为走过

	int sum = 1, res = 0;  //res存连通块节点个数的最大值
	for (int i=h[u];i!=-1;i=ne[i])
	{
		int j = e[i];            
		if (!st[j])  //如果当前节点没有被遍历就遍历
		{
			int s = dfs(j);
	        res = max(res,s);

			sum += s;  //以某个节点为根节点的1连通块要把1这个节点自身也算上
		}
	}
	res = max(res,n-sum);

	ans = min(ans,res);
	return sum;
}
int main()
{
	cin >> n ;
	
	memset(h,-1,sizeof h);  //头节点初始化
	
	for (int i = 0; i < n-1; i++)
	{
		int a, b;
		cin >> a >> b;

		add(a, b), add(b,a);  //因为是无向边,所以要添加两条边
	}
	dfs(1);
	cout << ans << endl;
	return 0;
}

image.png