P1395 会议 - 树的重心

P1395 会议 - 树的重心解法

解题思路

这道题目要求找到一个村庄中的最佳会议地点,使得所有村民到该地点的距离之和最小。这实际上是经典的树的重心问题,因为树的重心具有所有节点到它的距离之和最小的性质。

关键步骤:

  1. 寻找树的重心:通过DFS遍历树,计算每个节点的子树大小,并找出删除后能使最大子树最小的节点

  2. 计算最小距离和:以找到的重心为根,再次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;
}

 

posted @ 2025-06-13 16:20  CRt0729  阅读(21)  评论(0)    收藏  举报