先来说一下树的重心的定义 : 一棵无根树上选择一个节点当作根节点,使得以这个节点为根的最大子树的节点数最少。这样的话就可以使得这棵树趋于平衡,在一些点分治或者其他算法中可以避免最坏的 n^2 的情况使得复杂度稳定为nlogn。
关于树的重心的求法:求法为一种从叶子到根的树形dp。具体做法为先随便取一个节点提到根上做一遍dfs,记录下每个节点包含该节点的子树大小(大小即为该节点所有儿子的大小加上1)和记录自己以该节点为根的父亲节点的编号。然后再从 1 到 n号节点扫描一边,看看每一个点最大子树的大小,还有总节点数减去该子树的大小,哪个最小哪个点就是重心。
说的可能有点啰嗦,看代码就好了,代码还是非常清晰的。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 3e4 + 10;
vector <int> G[maxn];
int n;
int ans = 0;
int sz[maxn] = {0};
int p[maxn] = {0};
int dfs (int u,int pre) {
sz[u] = 1;
p[u] = pre;
int k = G[u].size();
for (int i = 0;i < k; ++ i) {
if (G[u][i] == pre) continue;
sz[u] += dfs (G[u][i],u);
}
return sz[u];
}
int getroot () {
dfs (1,0);
int mx = 0;
int zx = 0;
for (int i = 1;i <= n; ++ i) {
mx = 0;
int k = G[i].size();
for (int j = 0;j < k; ++ j){
if (G[i][j] == p[i]) continue;
mx = max (sz[G[i][j]],mx);
}
mx = max (mx,n - sz[i]);
// cout << i << ' ' << mx << endl;
if (mx < ans) {
ans = mx;
zx = i;
}
}
return zx;
}
int main () {
ios_base :: sync_with_stdio(false);
int t;
cin >> t;
while (t --) {
memset (sz,0,sizeof (sz));
memset (p,0,sizeof (p));
cin >> n;
for (int i = 0;i <= n; ++ i)
G[i].clear();
ans = n;
for (int i = 0;i < n - 1; ++ i) {
int x,y;
cin >> x >> y;
G[x].push_back (y);
G[y].push_back (x);
}
int res = getroot();
// for (int i = 1;i <= n; ++ i)
// cout << sz[i] << ' ';
// cout << endl;
cout << res << ' ' << ans << endl;
}
return 0;
}