定义:
树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。
树的重心定义为树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。树可能存在多个重心。如下图,当去掉点1后,树将分成两个连通块:(2,4,5),(3,6,7),则最大的连通块包含节点个数为3。若去掉点2,则树将分成3个部分,(4),(5),(1,3,6,7)最大的连通块包含4个节点;第一种方法可以得到更小的最大联通分量。可以发现,其他方案不可能得到比3更小的值了。所以,点1是树的重心。
性质:
- 树上所有的点到树的重心的距离之和是最短的,如果有多个重心,那么总距离相等。
- 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
- 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
- 一棵树最多有两个重心,且相邻。
算法分析:
和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设 d [ i ] d[i] d[i] 表示以i为根的子树的结点的个数。不难发现 d [ i ] = ∑ d [ j ] + 1 d[i]=∑d[j]+1 d[i]=∑d[j]+1, j ∈ s [ i ] j∈s[i] j∈s[i]。 s [ i ] s[i] s[i]为i结点的所有儿子结点的编号的集合。程序也十分简单:只需要DFS一次,在无根树转有根数的同时计算即可,连记忆化都不需要——因为本来就没有重复计算。
那么,删除结点i后,最大的连通块有多少个呢?结点i的子树中最大有 m a x ( d [ j ] ) max({d[j]}) max(d[j])个结点,i的“上方子树”中有 n − d ( i ) n-d(i) n−d(i)个结点,这样,在动态规划的过程中就可 以顺便找出树的重心了。
以上内容来自《算法竞赛入门经典》
POJ 1655 Balancing Act(求重心)
Balancing Act
题意:给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的.
Sample Input
1
7
2 6
1 2
1 4
4 5
3 7
3 1
Sample Output
1 2
#include<string.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<double,ll>pdl;
#define debug(x) cerr<<"# "<<x<<endl
const ll N=20005;
const ll base=137;
const ll mod=2147483647;
const int INF = 1<<30;
//const double INF=double(INT_MAX*1.0);
ll head[N];//链式前向星
ll son[N];//son[i]表示以i为根的子树节点个数
ll cnt,n;
ll ans,size;
bool vis[N];//代替了其他做法里的fa (father)
struct Edge//链式前向星
{
ll to;
ll nex;
};
Edge edge[2*N];
inline void init()//初始化
{
cnt=0;
size=INF;
memset(vis,0,sizeof vis);
memset(head,-1,sizeof head);</