ZOJ - 3949 Edge to the Root——树形dp

本文详细介绍了如何使用树形DP算法解决一类特定问题:通过递归地计算子树的节点数量及更新节点间的距离之和来求解最优解。文章通过具体的示例解释了算法的实现过程,并提供了一段完整的C++代码实现。

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

首先用dp1【i】维护一下【以节点i为根节点的子树】的节点数(包括i)

然后从根节点向下递推,用dp2【i】表示在根节点和i节点之间连边以后的距离之和,那么我们从节点u推到他的子节点v时,距离之和首先要在dp2【u】的基础上减去dp1【v】,然后要计算v节点到根节点这条链上一半节点的距离变化,举个例子,假设1为根节点,4为v节点,链为1->2->3->4,那么之前说的一半节点就是指3和4两个节点,如果链为1->2->3->4->5,那么之前说的一半节点就是4和5两个节点,一半节点本质上就是将连线从u改到v后距离增大的那些节点,最后将一半节点的距离变化加到之前的结果上就是当前的结果了

然后注意一下边界以及向下取整之类的问题

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 2e5 + 10;
const long long INF = 1e12;
int T, n;
vector<int> G[maxn];
long long sum, dp1[maxn], dp2[maxn], dep[maxn];
void dfs1(int u, int f, int d) {
    sum += d - 1;
    dp1[u] = 1;
    for (int i = 0; i < (int)G[u].size(); i++) {
        int v = G[u][i];
        if (v == f) continue;
        dfs1(v, u, d+1);
        dp1[u] += dp1[v];
    }
}
void dfs2(int u, int f, int d) {
    for (int i = 0; i < (int)G[u].size(); i++) {
        int v = G[u][i];
        if (v == f) continue;
        dep[d+1] = v;
        dp2[v] = dp2[u] + dp1[dep[(d+2)/2+1]] - 2*dp1[v];
        dfs2(v, u, d+1);
    }
}
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) G[i].clear();
        int u, v;
        for (int i = 1; i < n; i++) {
            scanf("%d %d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        sum = 0;
        dfs1(1, 0, 1);
        dp2[1] = sum;
        dep[1] = 1;
        for (int i = 0; i < (int)G[1].size(); i++) {
            int v = G[1][i];
            dp2[v] = sum;
            dep[2] = v;
            dfs2(v, 1, 2);
        }
        long long ans = INF;
        for (int i = 1; i <= n; i++) ans = min(ans, dp2[i]);
        printf("%lld\n", ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值