树的重心,也叫树的质心,可以这样定义,对于一棵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
*/