算法学习打卡day42|动态规划:0 - 1背包问题

背包问题

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

  • 看看这些问题怎么解决:

  • 背包问题五部曲

    1. 确定dp数组含义
    2. 写出递推公式经典背包问题递推公式:
      • 这里有三种写法:
        1. 二维数组写法:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        2. 由二维数组优化的一维数组写法:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
        3. 对于排列问题递推公式的写法:dp[j] += dp[j - nums[i]]
    3. dp数组初始化,这个很重要,初始化写不对,会直接导致结果不对,就像排列问题一样
    4. 遍历顺序的问题:从前往后遍历还是从后往前,先遍历物品还是先遍历背包
    5. 根据递推公式推出结果,用于比对代码结果
  • 灵魂三问会不会?

    • 怎么实现一个纯二维数组的0-1背包?如果写出来了,然后再问为什么两个for循环的嵌套顺序这么写?反过来写行不行?再讲一讲初始化的逻辑。
      1. 二维数组的话就是行代表物品,第i行代表可以从0-i之间选物品,列代表背包容量即背包容量上限,数组元素代表最大价值。
      2. 初始化:由于背包容量为0时价值肯定为0,所以第一列初始化为0,然后第一行只要背包容量大于等于物品0时,之后的列都需要赋值为value[0],**为什么第一行需要单独赋值?**从递推公式可以看出二维数组求解的时候需要依赖 i - 1 的结果,所以第一行必须先赋值。其余元素赋值为0即可。
      3. 遍历顺序:二维数组的话,先遍历背包还是物品都是可以的,因为不论先遍历哪个维度,dp[i][j]的左上方元素肯定都被赋值过了。
    • 然后要求实现一个一维数组的01背包,最后再问,一维数组的0 - 1背包,两个for循环的顺序反过来写行不行?为什么?
      1. 实现一维数组的话,其实原理还是和二维数组一样,就是把二维换成了一维,我们的数组元素dp[i]其实开始的时候存的都是i - 1的结果,那么每次求 i 的结果的时候和dp[i]做比较放入较大者即可。
      2. 初始化:一维数组初始化就不需要单独先赋值了,全部赋值为0即可,因为是和dp[j]去比较,那么当i等于0时,是和初始值比较,而当 i 等于 1,是和i等于0时的结果比较,每次结果都是可读取的。
      3. 遍历顺序,这里有两点:
        • 遍历背包的时候需要倒序遍历,因为如果正序遍历的话,由于我们需要使用i - 1时候的结果,那么正序遍历会把上一组的结果覆盖导致后面的元素拿到的dp[j - wegiht[i]]已经不是上一组的而是当前组的值了,而倒序遍历则可以先使用左侧的元素。
        • 一维数组的整体遍历顺序,只能先物品后背包,因为我们每次知道一维数组写法其实是二维数组优化来的,需要用到左上方元素,那先物品后背包肯定没问题的,我们来看下先背包后物品,如果是先背包的话,由于背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。如果写成正序的话,就会面临物品重复放入的问题,因为dp[j]已经是上一组放入0-i物品的dp[j],而不再是真正的dp[j]了。
  • 以下是0-1背包不同维度上的应用

    • 纯 0 - 1 背包 (opens new window)是求 给定背包容量 装满背包 的最大价值是多少。
    • 分割等和子集 (opens new window)是求 给定背包容量,能不能装满这个背包。
      • 这个题知道背包容量是数组总和的一半就是直接套模版了,当然对于这种数组题每个元素的容量和价值都是对应元素值,所以weight[i]和values[i]都是nums[i]。
    • 最后一块石头的重量 II (opens new window)是求 给定背包容量,尽可能装,最多能装多少
      • 这道题和上题类似,石头怎么碰剩下的最少?当然是两边十分接近的时候,而且最好是相等的时候,所以我们就确定上界也就是背包容量是sum/2,由于可能是奇数,所以target一定小于等于右边。求完后,取差值即可。
    • 目标和 (opens new window)是求 给定背包容量,装满背包有多少种方法。
      • 这道题是属于排列问题了,递推公式和传统背包有点区别,dp数组元素代表符合条件的组合数而不是价值。
    • 一和零 是求给定背包容量,装满背包最多有多少个物品。
      • 这道题和上面其它题又不一样了,是要求符合条件的最大子集问题,但是给了背包容量且容量是二维的,那么其实写法还是模版写发,dp数组代表最大子集的元素个数,一定要牢记,所以我们每次取的是dp[i - zero_num][j - one_num]只有这样才不会超出背包容量,且子集数最大。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值