树的重心详解(C++)

树的重心又叫树的质心:对于一颗n个节点的无根树,找到一个点,使得把树变成以该节点为根的有根树时,最大子树的节点数最少。换句话说,删除这个节点后最大连通块(一定是树)的节点数最少。

首先要知道什么是树的重心,树的重心定义为:找到一个点,其最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 实际上树的重心在树的点分治中有重要的作用, 可以避免N^2的极端复杂度(从退化链的一端出发),保证NlogN的复杂度, 利用树型dp可以很好地求树的重心.

题目表述

树的重心定义为树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。树可能存在多个重心。如图所示:

树的重心详解(C++)_第1张图片

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的最大联通分量的节点个数

树的重心详解(C++)_第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 

你可能感兴趣的:(#,树)