poj 1655 Balancing Act 求树的重心(详解树重心求法)

本文介绍了树的重心概念及其在算法中的应用,通过树形DP方法寻找树的重心,以达到平衡树的目的,并提供了一个清晰的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先来说一下树的重心的定义 : 一棵无根树上选择一个节点当作根节点,使得以这个节点为根的最大子树的节点数最少。这样的话就可以使得这棵树趋于平衡,在一些点分治或者其他算法中可以避免最坏的 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值