0/1背包与完全背包差异分析

定义差异分析

  1. 0/1背包问题:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
  2. 完全背包问题: 有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],价值是value[i] 。每件物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。

对于每一件物品,0/1背包问题是放和不放的问题,完全背包是,放多少件的问题(可以是0)

代码实现分析

为了易于理解,这里的代码没有使用滚动数组(dp数组压缩成一维)

/**
 * 0/1背包问题:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],价值是value[i] 。
 *      每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
 * @param weights weight[i]第i件物品的重量
 * @param values value[i]第i件物品的价值
 * @param n 物品数量
 * @param w 背包容量
 * @return 最大价值
 */
int zeroOneBackpack(int weights[], int values[], int n, int w) {
    // 1. dp[i][j] 从前i中材料中任选,装满容量为j的背包,最大的价值
    int dp[][] = new int[n][w + 1];

    /*
    2. 递推公式 根据是否放入物品i分两种情况
        1)不放入物品i,那么dp[i][j] = dp[i - 1][j]
            也就是从前i - 1中材料中任选,装满容量为j的背包,最大的价值
        2)放入物品i,那么dp[i][j] = values[i] + dp[i - 1][j - weights[i]]
            也就是放入物品i后,剩余的空间为j - weights[i]
            从i - 1中材料中任选,装满容量为j - weights[i]的背包,最大的价值为dp[i - 1][j - weights[i]]
            dp[i][j]为两个之和,即values[i] + dp[i - 1][j - weights[i]]
        3)如果容量j小于物品i的重量weight[i],那么就不需要考虑第二种情况
        dp[i][j] = dp[i - 1][j];
        if (j >= weights[i])
            dp[i][j] = max(dp[i][j], values[i] + dp[i - 1][j - weights[i]])
    */

    // 3. 初始化
    for (int j = weights[0]; j <= n; j ++) {
        dp[0][j] = values[0];
    }

    // 4. 递推顺序
    for (int i = 1; i < n; i ++) {
        for (int j = 1; j <= n; j ++) {
            dp[i][j] = dp[i - 1][j];
            if (j >= weights[i]) {
                dp[i][j] = max(dp[i][j], values[i] + dp[i - 1][j - weights[i]]);
            }
        }
    }

    return dp[n - 1][w];
}
/**
 * 完全背包问题:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],价值是value[i] 。
 *      每件物品可以选多次(包含0),求解将哪些物品装入背包里物品价值总和最大。
 * @param weights weight[i]第i件物品的重量
 * @param values value[i]第i件物品的价值
 * @param n 物品数量
 * @param w 背包容量
 * @return 最大价值
 */
int completeBackpack(int weights[], int values[], int n, int w) {
    // 1. dp[i][j] 从物品0-i中任取,每个物品能取无限次
    //    装满容量为j的背包,能获取的最大价值
    int dp[][] = new int[n][w + 1];

    /*
    2. 递推公式 根据是否放入物品i分两种情况
        1)不放入物品i,那么dp[i][j] = dp[i - 1][j]
            也就是从前i - 1中材料中任选,装满容量为j的背包,最大的价值
        2)放入物品i,那么dp[i][j] = values[i] + dp[i - 1][j - weights[i]]
            也就是放入物品i后,剩余的空间为j - weights[i]
            再从i中材料中任选,装满容量为j - weights[i]的背包,最大的价值为dp[i][j - weights[i]]
            dp[i][j]为两个之和,即values[i] + dp[i][j - weights[i]]
        3)如果容量j小于物品i的重量weight[i],那么就不需要考虑第二种情况
        dp[i][j] = dp[i - 1][j];
        if (j >= weights[i])
            dp[i][j] = max(dp[i][j], values[i] + dp[i][j - weights[i]])
    */

    // 3. 初始化
    for (int j = 0; j <= w; j ++) {
        // 背包能放多少个物品0 (j / weights[0])
        dp[0][j] = (j / weights[0]) * values[0];
    }

    // 4. 递归顺序
    for (int i = 1; i < n; i++) {
        for (int j = 0; j <= w; j++) {
            dp[i][j] = dp[i - 1][j];
            if (j >= weights[i]) {
                // dp[i][j] = max(dp[i][j], values[i] + dp[i - 1][j - weights[i]]);
                // 上一行注释是0/1背包的递推公式
                // 0/1背包是values[i] + dp[i - 1][j - weights[i]]
                // 完全背包是values[i] + dp[i][j - weights[i]]
                // 0/1背包,如果我放入物品i,剩余的容量为j - weights[i]
                //		因为物品i已经放入了,那么我需要找的是从物品0 ~ i-1中任选,能获取的最大价值
                //		所以是dp[i - 1][j - weights[i]]
                // 完全背包,如果我放入物品i,剩余的容量为j - weights[i]
                //		此时放入了物品i,但因为每一件物品可以放入多次,那么我需要找的是,
                //		从物品0 ~ i中任选,能获取的最大价值,也就是dp[i][j - weights[i]]
                // 综上,0/1背包与完全背包在于我放入物品i后,剩余的容量能放的物品分别为[0, i-1]和[0, i]
                dp[i][j] = max(dp[i][j], values[i] + dp[i][j - weights[i]]);
            }
        }
    }

    return dp[n - 1][w];
}

总结

  1. dp数组定义:dp[i][j]从物品0-i中任取,装满容量为j的背包,能获取的最大价值
  2. 0/1背包,放入了物品i后,剩余的容量只能放物品[0, i - 1];完全背包,放入了物品i后,剩余的容量能放物品[0, i],也就是物品i能够重复放入。
  3. 携带研究材料 - 0/1背包问题
  4. 携带研究材料 - 完全背包问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

it00zyq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值