树的重心求法+POJ 1655

树的重心,也叫树的质心,可以这样定义,对于一棵n个结点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小,换句话说,删除这个点之后的最大连通块(一定是树结构)的结点数最小
———刘汝佳

本文是对刘汝佳的树的重心讲解的衍生和解释
求法是和树的最大集问题类似的,先选一个点作为根,然后把无根树转化为有根树,设dp[i]表示以i为根的结点个数,不难发现

j=son[i]dp[j] ) +1

显然可以一遍DFS进行求解,那么删除结点i后,最大的连通块有多少个节点呢,结点i的子树中最大的是max(dp[j]),那么i的上方子树为n-max(dp[i]),其原因是我们可以想象一个点,其上的父亲以及上面的所有点(其实是除去i及i的所有子点的点),将其想象成一个i的子树,这样一来,改点删去之后最大的联通块的size就是max(max(dp[j]),n-dp[i]);
所以可以在dp的过程中找出树的重心

代码如下

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#define MAXN 100000+10
using namespace std;
int n,m;
struct Line{
    int from,to,nxt;
}line[MAXN];
int head[MAXN],tail,son[MAXN];
int ans=0x7fffffff,loc=0x7fffffff;
bool vis[MAXN];
void add_line(int from,int to){
    tail++;
    line[tail].from=from;
    line[tail].to=to;
    line[tail].nxt=head[from];
    head[from]=tail;
} 
void dpp(int u){
    son[u]=0;
    vis[u]=true;
    int temp=-0x3f3f3f;
    for(register int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(!vis[v]){
            dpp(v);
            son[u]+=son[v];
            temp=max(temp,son[v]);
        }
    }
    son[u]++;
    temp=max(temp,n-son[u]);
    if(temp<ans||temp==ans&&u<loc){
        ans=temp;
        loc=u;
    }
}
int main(){
    //freopen(".txt","r",stdin);
    //freopen(".out","w",stdout);
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=m;i++){
        int from,to;
        scanf("%d%d",&from,&to);
        add_line(from,to);
        add_line(to,from); 
    }
    dpp(1);
    printf("%d %d\n",ans,loc); 
    return 0;
}

这里写图片描述

上述是思路,然后写了一道POJ的裸题 POJ1655

1A代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#define MAXN 100000+10
using namespace std;
int n,m;
struct Line{
    int from,to,nxt;
}line[MAXN];
int head[MAXN],tail,son[MAXN],T;
int ans=0x7fffffff,loc=0x7fffffff;
bool vis[MAXN];
void add_line(int from,int to){
    tail++;
    line[tail].from=from;
    line[tail].to=to;
    line[tail].nxt=head[from];
    head[from]=tail;
} 
void dpp(int u){
    son[u]=0;
    vis[u]=true;
    int temp=-0x3f3f3f;
    for(register int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(!vis[v]){
            dpp(v);
            son[u]+=son[v];
            temp=max(temp,son[v]);
        }
    }
    son[u]++;
    temp=max(temp,n-son[u]);
    if(temp<ans||temp==ans&&u<loc){
        ans=temp;
        loc=u;
    }
}
int main(){
    //freopen(".txt","r",stdin);
    //freopen(".out","w",stdout);
    scanf("%d",&T);
    while(T--){
         scanf("%d",&n);
         loc=ans=0x7fffffff;
         memset(head,0,sizeof(head)),tail=0;
         memset(vis,false,sizeof(vis));
         for(register int i=1;i<=n-1;i++){
             int from,to;
             scanf("%d%d",&from,&to);
             add_line(from,to);
             add_line(to,from); 
         }
         dpp(1);
         printf("%d %d\n",loc,ans); 
    }
    return 0;
}
/*
2
3
1 2 2 3
4
2 1
2 3
3 4
1 3
*/

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值