树形DP 树的重心
给定一颗树,树中包含 \(n\) 个结点(编号 \(1 \sim n\))和 \(n-1\) 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 \(n\),表示树的结点数。
接下来 \(n-1\) 行,每行包含两个整数 \(a\) 和 \(b\),表示点 \(a\) 和点 \(b\) 之间存在一条边。
输出格式
输出一个整数 \(m\),表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
\(1 \le n \le 10^5\)
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
参考:【E32 树形DP 树的重心】

如何判断出 “剩余各个连通块中点数的最大值最小” ?
只需要判断,假设删除结点 \(u\),则遍历 \(u\) 的所有子树找到最大子树,记为 \(size\)。
那么 \(u\) 上面的子树怎么算呢?

从该图可以看出,我们可以找到以 \(u\) 为根的最大子树,其值为 \(sum\),由于多了一个 \(u\),可以得到 \(sum = size +1\)。
\(u\) 上面的子树结点数即为 \(n-sum\)。
最后来一手 ans = min(ans, max(size, n - sum))
收工。
#include <bits/stdc++.h>
using namespace std;const int N = 1e5 + 5;
const int M = 2e5 + 5;
int n, ans = N;// ans为各个连通块中点数的最大值,初始为N最大
int idx, h[N];
struct edge {int v, ne;};
edge e[M];void add(int a, int b)
{e[idx] = {b, h[a]};h[a] = idx++;
}int dfs(int u, int fa)
{int size = 0;int sum = 1;// 记录以u为根的最大字数结点数,若sum=1,说明为叶子结点for (int i = h[u]; ~i; i = e[i].ne) {int v = e[i].v;if (v == fa) continue;// 避免向上查找int s = dfs(v, u);// s记录以v为根的子树的结点数size = max(size, s);// 记录u的最大子树的结点数sum += s;// 累加u的各个子树的结点数}ans = min(ans, max(size, n - sum));return sum;
}int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);memset(h, -1, sizeof(h));cin >> n;for (int i = 1; i < n; i++) {int a, b; cin >> a >> b;add(a, b);add(b, a);}dfs(1, 0);cout << ans << '\n';return 0;
}