【动态规划学习笔记】01背包理论基础

01背包

标准的01背包问题:

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

暴力法:回溯搜索,O(2^n)

使用动态规划

1.二维dp数组

实现步骤:

1.确定dp数组及其下标含义

dp[i] [j] 代表 从0到i任选物品,放进容量为j的背包所得的最大价值

2.确定递推公式

对于当前点有两种情况

(1)放进背包

此时的价格变为dp[i] [j] = dp[i- 1] [j - weight[i]] + values[i]

(2)不放进背包

当前剩余容量不满足物品要求dp[i] [j] = dp[i - 1] [j]

最后要求价值最高。所以要在两者之间取最大值

递推数组:dp[i] [j] = max(dp[i-1] [j], dp[i-1] [j-weight[i]] + values[i] )

3.初始化dp数组

根据递推公式可知一个点是由该点左边和上边的点推导出来,先初始化dp[0] [j]一行和dp[i] [0]一列

dp[0] [j] 一行

满足第一个物品重量的初始化化为第一个物品的价值,不满足的初始化为0

dp[i] [0] 一列

没有重量,初始化为0即可

4.确定遍历顺序

因为当前点的状态是由上方和左上方的状态推导,所以一行一行遍历和一列一列遍历都可以

5.举例推导dp数组

可以举个例子或者根据测试用例和自己的递推公式进行推导,验证自己前面几步的正确性,或者在提交不过后打印日志,手动推导dp数组来debug

实现代码(以Java为例):

public class demo1 {
    //01背包理论实践
    //二维dp数组
    public static void main(String[] args) {
        int[] weight = {3, 4, 1};
        int[] value = { 20, 30, 10};
        int bagSize = 5;
        System.out.println(bag_01(weight, value, bagSize));
    }
​
    /**
     *     有n件物品和一个最多能背重量为w 的背包。
     *     第i件物品的重量是weight[i],得到的价值是value[i]
     *     。每件物品只能用一次,求解将哪些物品装入背包里物品
     *     价值总和最大。
     */
    public static int bag_01(int[] weight, int[] value, int bagSize) {
        int n = weight.length;
        int m = bagSize;
        //行代表背包的容量,从0开始
        //列表示物品,从0小标开始
        int dp[][] = new int[n + 1][m + 1];
        for(int i = 0; i <= n; i++) {//初始化列
            dp[i][0] = 0;
        }//初始化列都为0可以省略,为了思路更有逻辑在这里列出来
        for(int j = 1; j <= m && weight[0] <= j ; j++) {
            dp[0][j] = value[0];
        }
        //遍历顺序按行按列两种都行
        for(int i = 1; i < n; i++) {
            for(int j = 1; j <= m; j++) {
                if(j < weight[i]) dp[i][j] = dp[i - 1][j];
                else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }
        //打印一下dp数组
        for(int i = 0; i < n; i++) {
            for(int j = 0; j <= m; j++) {
                System.out.print(dp[i][j] + "  ");
            }
            System.out.println();
        }
        System.out.println("===============");
        return dp[n - 1][m];
    }
}
​

2.一维dp数组(滚动数组)

因为之前是靠上一层推导出下一层,可以将二维矩阵压缩成一个一维dp数组,就是滚动数组,不断利用之前的值,即将上一次已经计算好的值拷贝到单层,变成一维数组

实现步骤:

1.确定dp数组及其下标含义

将前文所述的二维数组dp[i] [j]压缩为滚动数组dp[j],dp[j]代表了重量为j的背包所能容纳的最大价值

2.确定递推公式

(1)不放入背包,即背包容量小于物品

dp[j] = dp[j],因为此时的dp[j]就相当于二维的dp[i - 1] [j]

(2)放入背包

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

3.初始化dp数组

动态规划中初始化这步也很重要,需要想清楚

这题dp[j]代表重量为j的背包能容纳的最大价值,一开始都初始化为0就行,表示没有物品放入背包

4.确定遍历顺序

首先肯定要遍历每一个物品,物品在外层循环

内层循环遍历背包,注意遍历顺序,如果是从左到右遍历会出现一个物品重复使用的情况,因为此时外层循环是相同的物品,从左往右遍历使用了前面的状态相当于使用了相同的物品,所以要从右往左遍历。这里可以有一个优化的点,即内层循环不考虑容量小于当前物品重量的背包

5.举例推导dp数组

实现代码(以Java为例):

public class demo2 {
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagSize = 4;
        System.out.println(bog_02(weight, value, bagSize));
    }
    //二维矩阵压缩为一维数组,滚动数组
    public static int bog_02(int[] weight, int[] value, int bagSize) {
        int n = weight.length;
        int m = bagSize;
        //初始化,默认就是0
        int[] dp = new int[m + 1];
        for(int i = 0; i < n; i++) {
            for(int j = m; j >= weight[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for(int j = 0; j <= m; j++) {
                System.out.print(dp[j] + "  ");
        }
        System.out.println();
        System.out.println("==============");
        return dp[m];
    }
}

理解01背包问题对于学习动态规划有重要的意义

以上内容是我对在代码随想录学习01背包问题知识点的总结以及收获,希望以上内容能帮助到大家

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值