背包问题【代码+注释】01背包、完全背包、多重背包——动态规划经典

描述:有n个物品,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?

思路:
  1. 明确dp数组的意义:dp[i] [j]表示,当我现在只有前i个物品以及j个空间时,背包能带走的最大价值

  2. 寻找递推关系:当遍历到某个物品时,我们所面临的问题是,装不装这个物品

    • 装:上一个物品计算之后加上这个物品的价值,dp[i - 1] [j – weight[i]] + value[i]

    • 不装:上一个物品计算之后的价值,dp[i - 1] [j]

  3. 初始化dp数组:找到显而易见的初始值

当剩余空间为0,价值也为0:dp[i] [0] = 0

代码:
/**
 * 01背包问题
 * @param weight n个物品的重量
 * @param value n个物品的价值
 * @param total 背包总重量
 * @return 能带走的最大价值
 */
public int zeroAndOneBag(int[] weight, int[] value, int total){
    int n = weight.length;
    // dp[i][j]:有前i个物品,j个容量时的最大价值
    int[][] dp = new int[n + 1][total + 1];
    // 初始化
    for (int i = 0; i <= n; i++)  dp[i][0] = 0;
    // 递推
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= total; j++) {
            // 空间是否能装下当前物品
            if(j >= weight[i - 1]){
                // 能装下,就看你选择装或者不装
                dp[i][j] = Math.max(dp[i - 1][j - weight[i - 1]] + value[i - 1], dp[i - 1][j]);
            }else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[n][total];
}

描述:有n种物品,每种物品有无限个,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?

01背包中的问题只有一个区别,完全背包的物品可以放置多次,01问题中使用二维数组,就是为了控制不同的物品最多使用一次

代码
/**
 * 完全背包问题
 * @param weight n个物品的重量
 * @param value n个物品的价值
 * @param total 背包的所能承受的重量
 * @return 最大价值
 */
public int completeBag(int[] weight, int[] value, int total){
    // 物品的种数
    int n = weight.length;
    // dp[j] 表示有j个空间,所能带走的最大价值
    int[] dp = new int[total + 1];
    dp[0] = 0;
    for (int i = 0; i < n; i++) {
        for (int j = weight[i]; j <= total; j++) {
            dp[j] = Math.max(dp[j - weight[i]] + value[i], dp[j]);
        }
    }
    return dp[total];
}

描述:有n种物品,每种物品有amount[i]个,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?

完全背包类似,区别在于每种物品的个数是有限制的。用一个count[]数组来存储当前容量下,取得最大价值已经要用到本类物品多少个,如果当前物品没用完,则产生比较;如果当前物品用完了,即使装上它价值大,也不装它。

注:count[i]放入第i个物品的操作是基于count[j-weight[i]]放入的

/**
 * 多重背包
 * @param amount 每种物品的数量
 * @param weight 每种物品的重量
 * @param value  每种物品的价值
 * @param total  背包能承受的总重量
 * @return 最大价值
 */
public int multiple(int[] amount, int[] weight, int[] value, int total){
    int n = weight.length;
    int[] dp = new int[total + 1];
    dp[0] = 0;
    for (int i = 0; i < n; i++) {
        // count[j] 表示 当前容量已经用了当前种类物品的个数
        int[] count = new int[total + 1];
        for (int j = weight[i]; j <= total; j++) {
            // 当前种类的物品还没有用完
            if(count[j - weight[i]] < amount[i]){
                int temp = dp[j - weight[i]] + value[i];
                // 如果选了当前物品
                if(temp >= dp[j]){
                    dp[j] = temp;
                    // 放入第i个物品的操作是基于count[j-weight[i]]放入的
                    count[j] = count[j - weight[i]] + 1;
                }
                continue;
            }
            if(dp[j] == 0){
                dp[j] = dp[j - 1];
            }
            count[j] = count[j-1];
        }
    }
    return dp[total];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值