2020ICPC南京站 Monster Hunter 树上背包

2020ICPC南京站 Monster Hunter 树上背包


传送门:

题意

给 你 一 棵 树 , 你 可 以 去 掉 m 个 点 ( m ∈ [ 0 , n ] ) , 然 后 计 算 剩 余 结 点 的 贡 献 。 给你一棵树,你可以去掉m个点(m\in [0,n]),然后计算剩余结点的贡献。 m(m[0,n])
每 个 结 点 的 贡 献 为 它 本 身 的 价 值 加 上 它 所 有 直 接 相 连 儿 子 的 价 值 。 每个结点的贡献为它本身的价值加上它所有直接相连儿子的价值。
当 然 你 需 要 合 理 安 排 删 去 的 i 个 结 点 使 贡 献 最 小 化 。 输 出 每 个 i 对 应 的 贡 献 值 。 当然你需要合理安排删去的i个结点使贡献最小化。输出每个i对应的贡献值。 i使i

思路

有 树 、 节 点 、 贡 献 , 得 出 树 形 d p 。 有树、节点、贡献,得出树形dp。 dp

设 f [ i ] [ j ] 表 示 以 i 为 根 节 点 的 子 树 中 留 下 j 个 节 点 的 最 小 贡 献 。 设f[i][j]表示以i为根节点的子树中留下j个节点的最小贡献。 f[i][j]ij
题 意 表 示 , 必 须 杀 掉 父 节 点 才 能 杀 掉 子 节 点 , 所 以 父 节 点 是 否 存 活 也 很 关 键 。 即 : 题意表示,必须杀掉父节点才能杀掉子节点,所以父节点是否存活也很关键。即:
f [ 0 ] [ i ] [ j ] 表 示 删 掉 i 后 留 下 j 个 节 点 的 最 小 贡 献 。 f[0][i][j]表示删掉i后留下j个节点的最小贡献。 f[0][i][j]ij
f [ 1 ] [ i ] [ j ] 表 示 保 留 i 后 留 下 j − 1 个 节 点 的 最 小 贡 献 。 f[1][i][j]表示保留i后留下j-1个节点的最小贡献。 f[1][i][j]ij1

状 态 转 移 都 是 从 子 节 点 转 移 到 父 节 点 : 状态转移都是从子节点转移到父节点:
f [ 0 ] [ u ] [ j + k ] = m i n ( f [ 0 ] [ u ] [ j + k ] , f [ 0 ] [ u ] [ j ] + m i n ( f [ 0 ] [ v ] [ k ] , f [ 1 ] [ v ] [ k ] ) ) f[0][u][j+k]=min(f[0][u][j+k],f[0][u][j]+min(f[0][v][k],f[1][v][k])) f[0][u][j+k]=min(f[0][u][j+k],f[0][u][j]+min(f[0][v][k],f[1][v][k]))
f [ 1 ] [ u ] [ j + k ] = m i n ( f [ 1 ] [ u ] [ j + k ] , f [ 1 ] [ u ] [ j ] + m i n ( f [ 0 ] [ v ] [ k ] , f [ 1 ] [ v ] [ k ] + v a l [ v ] ) ) f[1][u][j+k]=min(f[1][u][j+k],f[1][u][j]+min(f[0][v][k],f[1][v][k]+val[v])) f[1][u][j+k]=min(f[1][u][j+k],f[1][u][j]+min(f[0][v][k],f[1][v][k]+val[v]))

第 二 个 为 什 么 要 加 上 v a l [ v ] , 因 为 题 意 说 对 于 一 个 节 点 的 贡 献 , 是 自 己 加 上 所 有 儿 子 的 权 值 。 第二个为什么要加上val[v],因为题意说对于一个节点的贡献,是自己加上所有儿子的权值。 val[v]

上 面 的 状 态 转 移 直 接 背 包 即 可 。 上面的状态转移直接背包即可。

Code

#include "bits/stdc++.h"
using namespace std;

typedef long long ll;

const int N = 5e3 + 10;

ll val[N];
int siz[N];
ll f[2][N][N];

vector<int> g[N];

void dfs(int u) {
    siz[u] = 1;
    f[1][u][1] = val[u];
    f[0][u][0] = 0;
    for(auto v : g[u]) {
        dfs(v);
        for(int j = siz[u];j >= 0; j--) {
            for(int k = siz[v];k >= 0; k--) {
                f[0][u][j + k] = min(f[0][u][j + k], f[0][u][j] + min(f[0][v][k], f[1][v][k]));
                f[1][u][j + k] = min(f[1][u][j + k], f[1][u][j] + min(f[0][v][k], f[1][v][k] + val[v]));
            }
        }
        siz[u] += siz[v];
    }
}

void solve() {
    int _; cin >> _;
    while(_--) {
        int n; cin >> n;
        for(int i = 0;i <= n; i++) {
            g[i].clear();
            for(int j = 0;j <= n; j++) {
                f[0][i][j] = f[1][i][j] = 1e18;
            }
        }
        for(int i = 2;i <= n; i++) {
            int u; cin >> u;
            g[u].eb(i);
        }
        for(int i = 1;i <= n; i++) cin >> val[i];
        dfs(1);
        for(int i = n;i >= 0; i--) {
            cout << min(f[0][1][i], f[1][1][i]) << " \n"[i == 0];
        }
    }
}

signed main() {
    solve();
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值