CF - 274B 树形dp

题意:

给出的一棵树,树上每个节点都都有一个值,现在可以每次选择树上的一块联通区域,要求这个区域中必须包含节点1,每次可以选择的合法区域进行所有节点值+1或-1的操作,问要使所有节点的值都变为0,最少需要几步操作?

思路:

一道树形DP。不妨以节点1为根,这样可以发现,要让每个节点的值都为0,而且每一步的操作都要求包含根,那么儿子节点一定不会晚于父亲节点变成0,所以我们可以从下到上进行dp,每次先求出子树所需要的代价,然后根据子树的情况dp出父亲的。
这里需要的注意的是,dp不能仅仅保存子树的操作数,这样显然无法进行转移,其实,对于一个节点来说所需要的操作步数,就是increase操作和decrease操作的和,那么我们可以设dp[u][0]表示increase操作的步数,dp[u][1]表示decrease操作的步数,这样就可以进行转移了,对于每个节点,我们只要求出在所有的儿子节点的dp,就可以推算出使它变成0最少需要增加多少,减少多少,即可完成状态转移。


代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;

vector <int> tree[MAXN];
ll dp[MAXN][2], a[MAXN];

void dfs(int u, int pre) {
    int cnt = tree[u].size();
    ll inmax = 0, demax = 0;
    for (int i = 0; i < cnt; i++) {
        int v = tree[u][i];
        if (v == pre) continue;
        dfs(v, u);
        inmax = max(inmax, dp[v][0]);
        demax = max(demax, dp[v][1]);
    }
    ll tmp = inmax - demax;
    if (a[u] + tmp >= 0) { dp[u][0] = inmax; dp[u][1] = a[u] + tmp + demax; }
    else { dp[u][0] = inmax - (a[u] + tmp); dp[u][1] = demax; }
}

int main() {
    //freopen("in.txt", "r", stdin);
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    for (int i = 1; i <= n; i++)
        scanf("%I64d", &a[i]);
    dfs(1, -1);
    printf("%I64d\n", dp[1][0] + dp[1][1]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值