定义: 找到一个点,删除它得到的森林中最大的子树节点数最少,那么这个点就是这棵树的重心。
性质: 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
求解重心: 只需比较分别删除每一个结点后形成的森林中最大子树的结点数,该指标最小的结点即为树的重心。删除某一结点x得到的子树可以分成两类,一类x的子树,另一类是整棵树中除去以x为根的子树的部分。计算第一类的数量只需知道x的儿子有多少个子孙,计算第二类的数量只需知道x有多少个子孙。
POJ 1655
给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的.
/*
树的重心
删除重心得到的森林中最大的子树节点数最少
复杂度O(m)
比较分别删除每一个结点后形成的森林中最大子树的结点数,
该指标最小的结点即为树的重心。
*/
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int INF = 0x7fffffff;
vector<int> g[20005];
int n;
int size,num;
int vis[20005];
int son[20005];
void dfs(int x)
{
vis[x] = 1;
int temp = 0; //temp表示删除节点x后形成的子树个数最大值
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( !vis[t] )
{
dfs(t);
son[x] += son[t] + 1;
//x的子孙个数等于其所有儿子的子孙个数加1
temp = max(temp,son[t]+1); //删去x后其儿子分别会形成树
}
}
temp = max(temp,n - son[x] - 1); //以x为根的树删去,剩下的为一棵新树
if( temp < size || ( temp == size && num > x ) )
{
size = temp;
num = x;
}
}
int main()
{
int t;
scanf("%d",&t);
while( t-- )
{
memset(vis,0,sizeof(vis));
memset(son,0,sizeof(son));
scanf("%d",&n);
for (int i = 1; i <= n; i++)
{
g[i].clear();
}
for (int i = 1; i < n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
size = INF;
num = 0;
dfs(1);
printf("%d %d\n",num,size);
}
return 0;
}