树的重心又叫树的质心:对于一颗n个节点的无根树,找到一个点,使得把树变成以该节点为根的有根树时,最大子树的节点数最少。换句话说,删除这个节点后最大连通块(一定是树)的节点数最少。
首先要知道什么是树的重心,树的重心定义为:找到一个点,其最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 实际上树的重心在树的点分治中有重要的作用, 可以避免N^2的极端复杂度(从退化链的一端出发),保证NlogN的复杂度, 利用树型dp可以很好地求树的重心.
树的重心定义为树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。树可能存在多个重心。如图所示:
1.当去掉点1后,树将分成两个连通块
(2,4,5)
(3,6,7)
则最大的连通块包含节点个数为3。
2.若去掉点2,则树将分成3个部分
(4)
(5)
(1,3,6,7)
最大的连通块包含4个节点;
......
第一种方法可以得到更小的最大联通分量。可以发现,其他方案不可能得到比3更小的值了,点1就是树的重心。
输入:第一行一个整数n,表示树的结点个数。(n<100)
接下来n-1行,每行两个数i,j。表示i和j有边相连。
输入:第一行一个整数n,表示树的结点个数。(n<100)
接下来n-1行,每行两个数i,j。表示i和j有边相连。
7
1 2
1 3
2 4
2 5
3 6
3 7
1
1
f[i]
表示以为根的子树的节点个数,显然有:f[i]=∑f[j]+1 (j是i的子树) 结论一
如图,以节点1为根节点,求节点2的最大联通分量的节点个数
我们删除节点2,可以得到3个联通分量:① ② ③
① ② 是节点2的子树,我们可以使用“结论一”递归求得
③是该节点上方的子树,该子树的节点数:
n-f[i]
我们所求的节点i的最大联通分量的节点数为:
dp(i) = max(f[j],n-f[i]) (j是i的子树)
#include "iostream"
#include "vector"
using namespace std;
#define N 100
vector G[N];
int n;
int dp[100];
int tot[100];
void read_tree();
void dfs(int u, int fa);
int main(){
read_tree();
int root = 2;//转化为有根数,根的位置是任意的,对结果没有影响
dfs(root,-1);
int minn = 100;
vector xx;
for (int i = 1; i <=n ; ++i) {
if(dp[i]>n;
for (int i = 0; i >u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
}
/**
* 将无根树转换为有根树,dfs过程中顺便计算下子树的节点数
* @param u 以u为有根树的根
* @param fa 节点的父节点
*/
void dfs(int u, int fa) {
tot[u] = 1;
for (int i = 0; i