树的重心

定义:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

性质:

性质 1 :树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。

性质 2 :把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。

性质 3 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

例题: POJ 1655

题目分析:

求树的重心的方法,我们把每棵树的所连接的最大子树求出来, 然后所有节点的最大子树中最小的那个就是树的重心。

关于求所有树的最大子树求法,我们可以求出每个点下方节点的数量,然后再求每个子树的时候把他的节点数量给记录一下。最后再反向求一下父亲节点的子树的数量就行了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;
typedef long long LL;
const LL INF = 1e9+7;
const LL MOD = 1e9+7;
const LL MAXN = 20005;
vector<vector<int> > G;
int dp[MAXN], num[MAXN], n;///该点所保存的最大子树,num代表该点的子节点的数量

void DFS(int root)
{
    num[root] = 1;
    int len = G[root].size();

    for(int i=0; i<len; i++)
    {
        int v = G[root][i];
        if(num[v] == 0)
        {
            DFS(v);
            dp[root] = max(dp[root], num[v]);
            num[root] += num[v];///下方节点的总个数
        }
    }
    dp[root] = max(dp[root], n-num[root]);
}

int main()
{
    int T, a, b;

    scanf("%d", &T);

    while(T --)
    {
        scanf("%d", &n);
        G.clear();
        G.resize(n + 3);
        memset(dp, -1, sizeof(dp));
        memset(num, 0, sizeof(num));
        for(int i=1; i<n; i++)
        {
            scanf("%d %d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        DFS(1);
        int ans = 1;

        for(int i=2; i<=n; i++)
        {
            if(dp[ans] > dp[i])
                ans = i;
        }
        printf("%d %d
",ans, dp[ans]);
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/chenchengxun/p/4956647.html