树的重心 + 板子

嘛是树的重心?

树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,
那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
所有的子树中最大的子树节点数最少,这句话需要细细的品味。

所有的子树中最大的子树节点数最少 是嘛意思?

在求树的重心的时候,我们需要删除一个点,至于为什么要这样做,我的拙见
是现在会求就行了,大佬们研究出来的算法就是让我们用的(不喜勿喷)。
那么我们究竟应该删除哪个节点以满足我们的要求呢?


我们可以按照树形 DP 的思想由下到上进行尝试,找到符合 
所有的子树中最大的子树节点数最少 这个条件的。
我们删除一个节点后会出现很多的分支,我们要使得这些分支中最大的最小。
用图来解释一下:

当我们删除 1 号节点时,各分支的大小为 3(3、6、7) 3(2、5、4)        最大为 3
当我们删除 2 号节点时,各分支的大小为 1(5)   1(4)  4(1、3、6、7)   最大为 4

我们要从 最大的里面找一个最小的,即删除一号节点的 3,所以这棵树的重心是 3.

树的重心有嘛性质?

树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
一棵树最多有两个重心,且相邻。

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 1e5 + 10;

int head[maxn],Next[maxn],edge[maxn],ver[maxn];

int Size[maxn],vis[maxn];

int tot = 0,u,v;

int n,min_port = INF,center;

void add(int u,int v) {
	ver[++ tot] = v,Next[tot] = head[u];
	head[u] = tot;
	return ;
}

void DFS(int u) {
	// 标记,初始化每个节点的大小为 1 
	vis[u] = 1,Size[u] = 1;
	// 存储要删除的节点的所有的子树中最大的一颗子树的大小 
	int max_port = 0;
	for(int i = head[u]; i; i = Next[i]) {
		int y = ver[i];
		if(vis[y]) continue;
		DFS(y);
		// 向上走 
		Size[u] += Size[y];
		// 比较得到最大的 
		max_port = max(max_port,Size[y]);
	} 
	// 得到删除当前节点后,节点数量最多的连通块 
	max_port = max(max_port,n - Size[u]);
	// 找到最大中的最小的 
	if(max_port < min_port) {
		min_port = max_port;
		center = u; 
	}
	return ;
}

int main(void) {
	scanf("%d",&n);
	for(int i = 1; i < n; i ++) {
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	// 将无根树转化成有根树 
	DFS(1);
	printf("min_port = %d\ncenter = %d\n",min_port,center);
	return 0;
} 

测试:

9
1 2
1 7
1 4
2 8
2 5
4 3
4 6
3 9


Answer:

posted @ 2020-04-28 11:36  IceSwords  阅读(191)  评论(0编辑  收藏  举报