动态树

sgu 134 这题说的是寻找这个树的重心 给你一个连通的无向图,他有N 个顶点和 N-1 条边 (一棵树)。现在你需要找到这棵树的重心。现在定义树的重心,树的每一个顶点有一个权值。考虑顶点k。如果从图中删除k号顶点(连带的边也一起被删除),剩下的图将只有 N-1 个顶点而且可能由多个连通分量组成。显然每一个连通分量还是一棵树。那么k号顶点的权值就是删除它以后剩下的连通分量中顶点数最多的顶点个数,删除k之后。这 N 个顶点中权值最小的就是重心。

此题要求我们求一棵树的重心。

给定一棵N个结点的树,求该树的所有重心。重心的定义如下:

删掉某结点i后,若剩余k个连通分量,那么定义d(i)为这些连通分量中结点数的最大值。

所谓重心,就是使得d(i)最小的结点i。

算法分析:

建立边表data。由于是无向的,因此边表长度为(N - 1) * 2。边表按照p1端点递增的顺序排序,以便计算每一个顶点的边表序号

树的基本操作:以结点1为根,计算出每个结点所在的子树的结点数。

枚举每一个结点,若将其删掉,那么考虑剩余的所有连通分量。

1、它的子树,其结点数可以直接调用。

2、它的上方子树,其结点数可通过n-1-减去所有子树的结点数算出。

这样,在其中选择d(i)最小的即可。时间复杂度:O(N),空间复杂度:O(N)

#include <cstdio>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;
vector<int>to[16005];
int sum[16005],d[16005],n;
void dfs(int cur, int per)
{
    int len = to[cur].size();
    for(int i = 0; i<len ; ++i)
        {
            int a = to[cur][i];
            if(a!=per){
                dfs(a,cur);
                sum[cur]+=sum[a];
                d[cur]=max(d[cur],sum[a]);
            }
        }
    sum[cur]++;
    d[cur]=max(d[cur],n-sum[cur]);
}
int main()
{

     scanf("%d",&n);
     memset(sum,0,sizeof(sum));
     memset(d,0,sizeof(d));
     for(int i=0; i<n-1; ++i)
     {
         int a,b;
         scanf("%d%d",&a,&b);
         to[a].push_back(b);
         to[b].push_back(a);
     }
     dfs(1,0);
     int maxv=n+1;
     for(int i=1; i<=n; ++i)
        if(d[i]<maxv)maxv=d[i];
     vector<int>ans;
     for(int i=1; i<=n; ++i)
        if(d[i]==maxv)ans.push_back(i);
     printf("%d %d\n",maxv,ans.size());
     for(int i=0; i<ans.size()-1; ++i)
        printf("%d ",ans[i]);
     printf("%d\n",ans[ans.size()-1]);
    return 0;
}
View Code

posted @ 2014-08-25 15:07  来自大山深处的菜鸟  阅读(170)  评论(0编辑  收藏  举报