AcWing 10. 有依赖的背包问题(图解版)


题目传送门

一、思路分析

二、实现代码

#include 

using namespace std;
const int N = 110;

int v[N];                  //体积
int w[N];                  //价值
int h[N], ne[N], e[N], idx;//邻接表保存树
int n, m;                  //物品个数和背包容量
int root;                  //记录根结点id
int p;                     //父节点id
int f[N][N];               //dp结果数组,描述以i为根结点的,在不超过j这个体积下的最大价值

//a是b的父节点
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

//因为u是根节点,所以u是必选的
void dfs(int u) {
    //枚举子树
    for (int i = h[u]; i != -1; i = ne[i]) { //遍历每个分组
        int son = e[i];//记录子树的根节点
        //先把孩子们都吃饱,然后我再来~
        dfs(e[i]);   //因为dfs是自下而上的,所以需要从下到上计算
        for (int j = m - v[u]; j >= 0; j--)//因为u是该树的根节点,所以必选,预留出u的空间
            for (int k = 0; k <= j; k++)   //枚举该子树分配到的体积
                f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);//加上该子树的值
    }
    for (int i = m; i >= v[u]; i--) f[u][i] = f[u][i - v[u]] + w[u]; //把物品u加进去,因为是根节点
    for (int i = v[u] - 1; i >= 0; i--) f[u][i] = 0;//如果无法容纳物品u,那他的价值为0;
}

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    //邻接表初始化操作
    memset(h, -1, sizeof(h));
    //读入物品个数和背包容量
    cin >> n >> m;
    //读入物品的体积、价值和父结点是谁
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i] >> p;
        if (p == -1) root = i;//根节点
        else add(p, i);   //构建树
    }
    //开始递归
    dfs(root);
    //输出结果,结果保存在以root为根的大树,最大体积是m的情况下的最优解
    printf("%d", f[root][m]);
    return 0;
}