poj 1655 Balancing Act 【树的重心】

知识点:树的重心


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


性质:

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

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

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


题目:poj 1655 Balancing Act


题意:给出一颗树,求树的重心点以及重心点删除后中的最大子树。


分析:

根据性质 1 ,我们容易想到枚举每个点作为 root ,然后求一次其子树的点和,然后每次找出一个最大的子树,最后最小的子树既为重心带你,O(n^2)的时间,TEL


一个删除后得到的子树,要么是从子节点过来的,要么是从父节点过来的,子节点的子树节点和的我们可以通过一次简单的dfs维护求得son【child】。

而父亲点过来的为总的节点数 N - son【father】 - 1

那么事情就变的简单了,我们只要维护dp【father】 = max(son【child】,N -  son【father】-1)


代码:


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
using namespace std;
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 20100;
vector<int> v[N];
int vis[N],dp[N],son[N];
int ans,asize;
int n;
void DFS(int s)
{
    vis[s] = 1;
    son[s] = 0;
    int blance = 0;
    int size = v[s].size();
    for (int j = 0;j < size;j++)
    {
        int u = v[s][j];
        if (vis[u]) continue;
        DFS(u);
        son[s] += son[u]+1;
        blance = max(blance,son[u]+1);
    }
    blance = max(blance,n - son[s] - 1);
    if (blance < asize || blance == asize && s < ans)
        ans = s,asize = blance;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {

        scanf("%d",&n);
        for(int i=0;i<n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        ans=1;
        Del(vis,0);Del(son,0);
        Del(dp,0);
        asize=0x3f3f3f3f;
        DFS(1);
        printf("%d %d\n",ans,asize);
        for(int i=0;i<=n;i++)
            v[i].clear();
    }
    return 0;
}



你可能感兴趣的:(Algorithm,线段树,DFS,树的重心,树形DP)