Balancing Act POJ - 1655 (树的重心模板题)
题目链接
题目大意:给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的.
Input
1
7
2 6
1 2
1 4
4 5
3 7
3 1
output
1 2
树的重心:
找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
树的重心的性质:
1.树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
2.把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
3.把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
怎么求解?
看代码吧
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 2e4 + 100;
const int inf = 0x3f3f3f3f;
vector<int> Edge[maxn];
int sonNum[maxn], sonMax[maxn];
//sonNum[i] 表示以i为根,这棵子树的节点个数的和
//sonMax[i] 表示以i为根的子树中,有最多节点数的子树的节点的个数
void dfs(int root, int pre) { //一遍dfs求出sonNum, sonMax
sonNum[root] = 1;
sonMax[root] = 0;
for(int i = 0; i < Edge[root].size(); i++) {
int to = Edge[root][i];
if(to == pre) continue;
dfs(to, root);
sonNum[root] += sonNum[to];
sonMax[root] = max(sonMax[root], sonNum[to]);
}
}
int main()
{
int t;
scanf("%d", &t);
int n;
while(t--) {
scanf("%d", &n);
for(int i = 0; i <= n; i++) Edge[i].clear();
for(int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
Edge[u].push_back(v);
Edge[v].push_back(u);
}
dfs(1, 0);
int Min = inf, pos;
for(int i = 1; i <= n; i++) {
//根据树的重心的定义,我们发现判断一个点是不是重心 只要把这个点去掉
//看它剩下的子树结点的个数的最大值是不是最小就ok了
//子树有两种:一个是往下的即sonMax[i],另一个是往上的 即n - sonNum[i]
if(Min > max(sonMax[i], n - sonNum[i])) {
Min = max(sonMax[i], n - sonNum[i]);
pos = i;
}
}
printf("%d %d\n", pos, Min);
}
}