P1395 会议 - 树的重心
P1395 会议 - 树的重心解法
解题思路
这道题目要求找到一个村庄中的最佳会议地点,使得所有村民到该地点的距离之和最小。这实际上是经典的树的重心问题,因为树的重心具有所有节点到它的距离之和最小的性质。
关键步骤:
-
寻找树的重心:通过DFS遍历树,计算每个节点的子树大小,并找出删除后能使最大子树最小的节点
-
计算最小距离和:以找到的重心为根,再次DFS计算所有节点到重心的距离之和
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; vector<int> g[N]; // 邻接表存储树结构 int n; // 村民数量(节点数) int s[N]; // s[x]表示以x为根的子树大小 int ans; // 存储最佳会议地点(重心) int dep[N]; // dep[x]表示节点x的深度(到根的距离) int dis; // 存储总距离和 int maxx = 1e9; // 初始化最大子树的最小值 // 第一次DFS:计算子树大小并找到重心 void dfs(int x, int fa) { s[x] = 1; // 初始化当前节点子树大小为1(包含自己) int cnt = 0; // 记录删除x后最大子树的大小 // 遍历所有邻接节点 for(int i = 0; i < g[x].size(); i++) { int y = g[x][i]; if(y == fa) continue; // 跳过父节点避免回溯 dfs(y, x); // 递归处理子节点 s[x] += s[y]; // 累加子树大小 cnt = max(cnt, s[y]); // 更新子节点方向的最大子树大小 } // 考虑父节点方向的子树(总节点数n - 当前子树大小s[x]) cnt = max(cnt, n - s[x]); // 更新重心候选:找最大子树最小的节点,若相同取编号小的 if(cnt < maxx || (cnt == maxx && x < ans)) { maxx = cnt; ans = x; } } // 第二次DFS:计算所有节点到重心的距离和 void dfs2(int x, int fa) { dep[x] = dep[fa] + 1; // 当前节点深度=父节点深度+1 // 遍历所有邻接节点 for(int i = 0; i < g[x].size(); i++) { int y = g[x][i]; if(y == fa) continue; // 跳过父节点避免回溯 dfs2(y, x); // 递归处理子节点 dis += dep[y]; // 累加子节点的深度(距离) } } int main() { cin >> n; // 构建树结构 for(int i = 1; i < n; i++) { int x, y; cin >> x >> y; g[x].push_back(y); g[y].push_back(x); } // 第一次DFS:找重心 dfs(1, 0); // 初始化根节点深度 dep[0] = -1; // 使根节点(dep[ans]=dep[0]+1=0) // 第二次DFS:以重心为根计算距离和 dfs2(ans, 0); // 输出结果:重心位置和最小距离和 cout << ans << " " << dis; return 0; }

浙公网安备 33010602011771号