树的重心

对于一个有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;
}

你可能感兴趣的:(树的重心)