码迷,mamicode.com
首页 > 其他好文 > 详细

树的重心

时间:2016-05-07 10:33:35      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:

对于一个有n个结点的无根树,找一个点作为根,使得最大子树的结点数最小,换句话说,删除这个点后最大连通块的结点数最小。
任选一个点作为根,设d(i)表示以i为根的子树的结点个数,那么:
技术分享
只需要一次dfs,连记忆化都不需要,因为没有重复计算。

现在重点来了:
删除结点i之后,最大连通块有多少个结点呢?
结点i的子树中最大的有max{d(j)}个结点,i的“上方子树”中有n-d(i)个结点!

poj1655 Balancing Act

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<deque>
#include<stack>
using namespace std;
const int maxn=20000+10;

vector<int>next[maxn];
int d[maxn];
int son[maxn];
int n;

void dfs(int cur,int fa){
    for(int i=0;i<next[cur].size();i++){
        int x=next[cur][i];
        if(x==fa)continue;
        dfs(x,cur);
        d[cur]+=d[x];
        son[cur]=max(son[cur],d[x]);//这里就记录最大子树的结点数
    }
    d[cur]++;
}

void maxcenter(){
    int ans=n+1,id;
    for(int i=1;i<=n;i++){
        int Max=max(n-d[i],son[i]);

//为啥下面代码就错了?不也是求最大子树的结点数吗
//        for(int j=0;j<next[i].size();j++){
//            int x=next[i][j];
//            if(x!=i)Max=max(Max,d[x]);
//        }
        if(ans>Max)ans=Max,id=i;
    }
    printf("%d %d\n",id,ans);
}

int main(){
    int T,x,y;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)next[i].clear();

        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&x,&y);
            next[x].push_back(y);
            next[y].push_back(x);
        }
        memset(d,0,sizeof(d));
        memset(son,0,sizeof(son));
        dfs(1,-1);
        maxcenter();
    }
    return 0;
}

树的重心

标签:

原文地址:http://blog.csdn.net/daniel_csdn/article/details/51331977

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!