背包之树形背包

树形背包——是背包的一种,它的特征是物品之间会有依赖关系,必须要先选一个物品,再选一个物品。

如果选择一个物品,则必须选择它的父节点。

第 i 件物品的体积为v[i] ,价值为 w[i],其父节点物品编号为p[i] ,其中根节点p[i] = -1 。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

在这个之前,我们要有一些基本知识;

链式向前星存图:

它的作用就是可以快速找到一个节点的所有子节点,与我们之前拿二维数组存储,优点是内存小,且速度也要快。

 void add(int a, int b) {
a为父节点,b为子节点。
        e[idx] = b;
e 用来存取子节点
        ne[idx] = he[a];
ne 用来存取上一个a的子节点的的idx,以方便查找
        he[a] = idx;
存取现在的子节点
        idx++;
    }

对于这些基础的数据结构,我们一般是想不到的,所有第一任务还是理解,然后把他们背下来。

就是一看就会,一写不会。

补充完前提条件后,我们可以去理理思路了。

首先因为选字节点之前一定要选父节点,我们就可以从下而上的进行dfs,先找出子节点的最好情况,然后进而推出父节点。

// 遍历节点 u 的所有子节点 x(分组背包遍历物品组)
        for (int i = he[u]; i != -1; i = ne[i]) {
            int x = e[i];
            // 递归处理节点 x
            dfs(x); 
            // 从大到小遍历背包容量(分组背包遍历容量)
            for (int j = c; j >= 0; j--) {
                // 遍历给节点 x 分配多少背包容量(分组背包遍历决策)
                for (int k = 0; k <= j - cv; k++) {
                    f[u][j] = Math.max(f[u][j], f[u][j - k] + f[x][k]);        
                }
            }
        }

接下不是全部代码:

class Solution {
    int N = 110, M = N * 2;
    int[] he = new int[N], e = new int[M], ne = new int[M];
    int[] vi, wi;
    int n, c, idx;
    // 定义 f[u][j] 为考虑以 u 为根的子树,背包容量不超过 j 的最大价值
    int[][] f = new int[N][N];

    // 链式向前星存图
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx;
        idx++;
    }

    void dfs(int u) {
        // 节点 u 的价值和体积
        int cw = wi[u], cv = vi[u];
        // 要选任一节点,必须先选 u,同时也限制了至少需要 cv 的容量
        for (int i = cv; i <= c; i++) f[u][i] += cw;

        // 遍历节点 u 的所有子节点 x(分组背包遍历物品组)
        for (int i = he[u]; i != -1; i = ne[i]) {
            int x = e[i];
            // 递归处理节点 x
            dfs(x); 
            // 从大到小遍历背包容量(分组背包遍历容量)
            for (int j = c; j >= 0; j--) {
                // 遍历给节点 x 分配多少背包容量(分组背包遍历决策)
                for (int k = 0; k <= j - cv; k++) {
                    f[u][j] = Math.max(f[u][j], f[u][j - k] + f[x][k]);        
                }
            }
        }
    }

    public int maxValue(int N, int C, int[] p, int[] v, int[] w) {
        n = N; c = C;
        vi = v; wi = w;
        Arrays.fill(he, -1);
        int root = -1;
        for (int i = 0; i < n; i++) {
            if (p[i] == -1) {
                root = i;
            } else {
                add(p[i], i);
            }
        }
        dfs(root);
        return f[root][c];
    }
}

感谢三叶姐。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值