給定 N 節點樹,刪除某節點後,剩餘連通分支中最大節點數最小化。該節點即為樹的重心。輸出重心編號及其最大分支大小。
void dfs_size(int u, int parent) {
sz[u] = 1;
for (int v : adj[u]) if (v != parent) {
dfs_size(v, u);
sz[u] += sz[v];
}
}
sz[u] = 以 u 為根的子樹節點數 (含自身)。
對節點 u,刪除後分支有兩類:
sz[v]N - sz[u]void dfs_ans(int u, int parent) {
int max_part = N - sz[u]; // 父側分支
for (int v : adj[u]) if (v != parent) {
dfs_ans(v, u);
max_part = max(max_part, sz[v]); // 子樹分支
}
if (max_part < best_val) {
best_val = max_part;
best_node = u;
}
}
#include
#include
#include
using namespace std;
const int MAXN = 110000;
struct Edge { int to, next; } e[MAXN*2];
int head[MAXN], ecnt;
int sz[MAXN], N, best_val, best_node;
void add_edge(int u, int v) {
e[ecnt] = {v, head[u]}; head[u] = ecnt++;
e[ecnt] = {u, head[v]}; head[v] = ecnt++;
}
void dfs1(int u, int p) {
sz[u] = 1;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == p) continue;
dfs1(v, u);
sz[u] += sz[v];
}
}
void dfs2(int u, int p) {
int mx = N - sz[u];
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == p) continue;
dfs2(v, u);
mx = max(mx, sz[v]);
}
if (mx < best_val) best_val = mx, best_node = u;
}
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d", &N);
memset(head, -1, sizeof(int)*(N+1));
ecnt = 0;
for (int i = 1; i < N; i++) {
int a, b; scanf("%d%d", &a, &b);
add_edge(a, b);
}
best_val = 0x3f3f3f3f;
dfs1(1, 0);
dfs2(1, 0);
printf("%d %d\n", best_node, best_val);
}
return 0;
}
memset(head, -1, ...) 只清有效範圍樹可能有 1 或 2 個重心 (相鄰)。若需輸出所有重心:
// dfs2 中
if (mx < best_val) { best_val = mx; centroids.clear(); centroids.push_back(u); }
else if (mx == best_val) { centroids.push_back(u); }
「樹的重心」是樹形 DP 經典入門模型:**換根 DP** 思想的最簡形式。第一遍自底向上聚合信息 (sz),第二遍自頂向下分發補全信息 (N-sz[u]),雙遍掃描即得全局最優。掌握此模板,可解決「樹的重心」、「樹的直徑」、「到所有點距離和」等一大類問題。