AcWing_10 有依赖的背包问题(树形DP+分组背包)

AcWing

10. 有依赖的背包问题

树形DP+分组背包。定义f[uu][m]表示以uu为根节点,容量为m的(子)树的最大价值。对于树上的某个节点,其最大的可能价值是节点本身的价值+选择取到的子树对应的价值之和。将节点的若干个子树看作若干个分组,可以选择取子树,也可以不取子树。将分组内按照所有可能的体积k划分f[vv][k]表示以vv为根节点的子树,体积为k时的最大价值。

状态转移方程为:

f[uu][j] = max{f[uu][j], f[uu][j-k] + f[vv][k]}, for a certain k.

考虑边界条件,背包初始的价值,即未选择任何一个分组时的价值,f[uu][m] = w[uu], m >= v[uu]. 此处,uu代表当前所有分组外的公共父节点,它仅用于树上操作。所以dp数组其实是省略了考虑前n个分组,这个维度的,完整的dp数组应该是f[uu][n][m].

所以对于每个确定的子树tr,其有三层循环,最外层枚举其根节点下面的所有子树,表示所有分组,第二层枚举容量(体积)j,容量应该从v[uu]枚举到m,确保一定包括了根节点,否则树tr的价值为0,第三层枚举分组内部的决策,具体地说,将每个分组划分为所有可能的体积(0j-v[uu],因为要排除掉背包初始占据的体积),通过这些所有可能的体积对应的最大价值,选出组内最优的决策。

通过dfs,自底向上地递归出问题的解。每次迭代都是一次单独的分组背包问题的求解,其以f的第一维uu作为标识。

#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 110, M = 110;
int v[N], w[N];
int h[N], e[N], ne[N], idx;
int f[N][M];
void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int uu){
    for(int j = v[uu]; j <= m; j++) f[uu][j] = w[uu];
    for(int i = h[uu]; ~i; i = ne[i]){
        int vv = e[i];
        dfs(vv);
        for(int j = m; j >= v[uu]; j--){
            for(int k = 0; k <= j - v[uu]; k++){
                f[uu][j] = max(f[uu][j], f[uu][j-k] + f[vv][k]);
            }
        }
    }
}
int main(){
    cin >> n >> m;
    memset(h, -1, sizeof h);
    int root;
    for(int i = 1; i <= n; i++){
        int p;
        cin >> v[i] >> w[i] >> p;
        if(p != -1) add(p, i);
        else root = i;
    }
    dfs(root);
    cout << f[root][m] << endl;
    return 0;
}