背包理论基础

01背包理论基础

1、01背包

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

2、示例

背包最大重量 4,物品重量[1, 3, 4],物品价值[15, 20, 30]。问背包能背的物品最大价值是多少?

3、二维dp数组01背包

1、dp数组含义:dp[i][j] 标识从物品 0-i 中任意取物品放入容量为 j 的背包中的价值的总和的最大值。那么可以有两个方向推出来dp[i][j],

  • 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)

  • 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值

2、所以递推公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

3、dp数组初始化:当背包的容量为0,即j=0时,dp[i][0] = 0; 其他情况 i 的情况都由 i-1 推导得出,所以 i = 0的情况需要初始化。

当只有物品0时,当背包容量大于等于物品0重量时,背包的价值为物品0的价值,初始化之后为:

01背包.png

对于其他的值 初始化为0 即可,后续会覆盖掉,所以最初声明dp数组时全部填充0方便些。

4、遍历顺序:由于dp[i][j]的值依赖于其左上角的值,所以遍历顺序为 从左到右,从上到下。对于先遍历物品或背包重量,虽然两个for循环遍历的次序不同,但是dp[i][j]所需要的数据就是左上角,根本不影响dp[i][j]公式的推导!

5、打印dp数组

public class TwoDP {
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagSize = 4;
        int n = solution(weight, value, bagSize);
        System.out.println(n);
    }

    private static int solution(int[] weight, int[] value, int bagSize) {
        int goods = weight.length;
        int[][] dp = new int[goods][bagSize + 1]; // 创建dp数组
        for (int[] ints : dp) {
            Arrays.fill(ints, 0);
        }
        // 初始化dp数组
        for (int j = weight[0]; j < bagSize + 1; j++) {
            dp[0][j] = value[0];
        }
        // 遍历dp数组
        for (int i = 1; i < goods; i++) {
            for (int j = 1; j < bagSize + 1; j++) {
                if (j < weight[i]) {
                    /**
                     * 当前背包的容量都没有当前物品i大的时候,是不放物品i的
                     * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                     */
                    dp[i][j] = dp[i - 1][j];
                } else {
                    int a = dp[i - 1][j];// 不放物品 i 的最大价值
                    int b = dp[i - 1][j - weight[i]] + value[i]; // 放入物品 i 的最大价值
                    dp[i][j] = Math.max(a, b);
                }
            }
        }
        for (int[] ints : dp) {
            System.out.println(Arrays.toString(ints));
        }
        return dp[goods - 1][bagSize];
    }
}

01背包理论基础(一维滚动数组)

1、dp数组含义

dp[j] 表示容量为j的背包,所容纳的物品的最大价值为dp[j]。

2、递推公式

dp[j] 的推导,1:不放新的物品,则最大价值不变为dp[j];2:放入物品 i,dp[j] = dp[j - weight[i]] + value[i],即容量为j-weight[i]的背包的最大价值加上当前物品的价值;然后取1 和 2 的最大值。

dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

3、初始化

当j=0,即背包容量为0时,dp[j]=0; 其他值 也初始化为0;

4、遍历顺序

一维dp数组的遍历顺序如下

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    }
}

遍历背包容量时需要倒序,倒序遍历是为了保证物品i只被放入一次!

public class TwoDP {
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagSize = 4;
        int m = solutionTwo(weight, value, bagSize);
        System.out.println("m-> " + m);
    }

    private static int solutionTwo(int[] weight, int[] value, int bagSize) {
        int goods = weight.length;
        int[] dp = new int[bagSize + 1];//dp[j] 表示容量为j的背包,所容纳的物品的最大价值为dp[j]。
        Arrays.fill(dp, 0); //初始化
        for (int i = 0; i < goods; i++) {
            for (int j = bagSize; j >= weight[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[bagSize];
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值