1. 程式人生 > >POJ 1655 +POJ 3107【求樹的重心】

POJ 1655 +POJ 3107【求樹的重心】

樹的重心:樹的重心也叫樹的質心。找到一個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹儘可能平衡。

換句話說,刪除這個點後最大連通塊(一定是樹)的結點數最小。

eg:

刪除1:子樹1:2、6    子樹2:4、5     子樹3:3、7     ans[1]=2;

刪除2:子樹1:6          子樹2:1、3、4、5、7           ans[2]=5;

刪除3:子樹1:7          子樹2:1、2、4、5、6            ans[3]=5;

刪除4:子樹1:5          子樹2:1、2、3、6、7            ans[4]=5;

刪除5:子樹1:1、2、3、4、6、7           ans[5]=6;

刪除6:子樹1:1、2、3、4、5、7           ans[6]=6;

刪除7:子樹1:1、2、3、4、5、6           ans[7]=6;

因此,樹的重心為cnt=min(ans[i],cnt)=2;

題目大意:給定一棵樹,求樹的重心的編號以及重心刪除後得到的最大子樹的節點個數size,如果有多個重心即size相同就選取編號最小的。

CODE:

const int maxn=500005;
int tot=0,n;
int ans,size;
int sx[maxn],head[maxn];
int vis[maxn];
struct edge
{
    int to,next;
} eg[maxn];
void add(int u,int v)
{
    eg[tot].to=v;
    eg[tot].next=head[u];
    head[u]=tot++;
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
}
void dfs(int u)
{
    vis[u]=1;
    sx[u]=1;
    int tmp=0;
    for(int i=head[u]; i!=-1; i=eg[i].next)
    {
        int v=eg[i].to;
        if(!vis[v])
        {
            dfs(v);
            sx[u]+=sx[v];
            tmp=max(tmp,sx[v]);
        }
    }
    tmp=max(tmp,n-sx[u]);
    if(size>tmp||size==tmp&&ans>u)
    {
        ans=u;
        size=tmp;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        int u,v;
        scanf("%d",&n);
        for(int i=1; i<n; i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        size=INF;
        dfs(1);
        printf("%d %d\n",ans,size);
    }
}


題目大意:給定一棵樹,求樹的所有重心,按照編號從小到大的順序輸出。

CODE:

const int maxn=100005;
struct node
{
    int to,next;
} eg[maxn];
int tot=0,num=0,n;
int vis[maxn],sx[maxn],ans[maxn];
int head[maxn];
void add(int u,int v)
{
    eg[tot].to=v;
    eg[tot].next=head[u];
    head[u]=tot++;
}
int size,cnt;
void dfs(int u)
{
    vis[u]=1;
    sx[u]=1;
    int tmp=0;
    for(int i=head[u]; i!=-1; i=eg[i].next)
    {
        int v=eg[i].to;
        if(!vis[v])
        {
            dfs(v);
            sx[u]+=sx[v];
            tmp=max(tmp,sx[v]);
        }
    }
    tmp=max(tmp,n-sx[u]);
    if(tmp<size)
    {
        //printf("&&&&&&\n");
        num=0;
        size=tmp;
        ans[num++]=u;
    }
    else if(tmp==size)
    {
        //printf("*********\n");
        ans[num++]=u;
    }
}
void init()
{
    tot=0,num=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(ans,0,sizeof(ans));
    size=INF;
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        int u,v;
        for(int i=1; i<n; i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1);
        sort(ans,ans+num);
        for(int i=0; i<num; i++)
        {
            if(i)
                printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

兩個都是模板題,主要還是對樹的重心的理解。簡單的DFS的應用,記錄每次刪除當前結點之後每個子樹的最大節點數,最小化最大節點數就是樹的重心,在一棵樹中,樹的重心可能不止一個。