本专栏已参加蓄力计划,感谢读者支持❤

往期文章

一. Java入门算法(贪心篇)丨蓄力计划
二. Java入门算法(暴力篇)丨蓄力计划
三. Java入门算法(排序篇)丨蓄力计划
四. Java入门算法(递归篇)丨蓄力计划
五. Java入门算法(双指针篇)丨蓄力计划
六. Java入门算法(数据结构篇)丨蓄力计划
七. Java入门算法(滑动窗口篇)丨蓄力计划
八. Java入门算法(动态规划篇1:初识动规)
九. Java入门算法(动态规划篇2:01背包精讲)


动态规划篇2

  • 往期文章
  • 01背包
    • 问题描述
    • 分析
    • 进阶

01背包

网上有非常多的文章对01背包进行讲解,变量名繁杂,对初学者不怎么友好。在这篇文章里,我尽量讲得简单,不作一些多余的赘述。

不会有人不知道背包是什么吧?


问题描述

把n种物品装进一个背包,物品 i 的重量是w[ i ],价值是c[ i ],背包的容量是M,求能装进背包的最大总价值。

注:w是存储n个物品的重量的数组,c是存储n个物品的价值的数组


分析

为啥叫01背包呢?因为对于每件物品,只有 不拿(0) 与 拿(1) 两种状态。

动态规划解01背包,关键是填二维表dp:

  • 行 i 指的是我们当前要考虑装 i 个物品
  • 列 j 表示的是背包剩余容量
  • dp [i] [j] 的值是指 i 个物品装进容量为 j 的背包的最大总价值

当 i 等于物品总量n、j 等于背包容量M的时候,dp [i] [j] 的值就是问题的解。下面根据例子输入,边填表边分析。

输入:n = 4, M = 10
w[] = [2, 3, 4, 7]
c[] = [1, 3, 5, 9]
(4个物品装进容量为10的背包)
  • 初始化第0行为全0,没有物品,不管容量多大,价值只能是0
  • 初始化第0列为全0,容量为0,装不下任何物品,价值只能是0


现在只考虑装 1 件物品(i = 1)

  • dp[1][1] = 0,容量为1的背包装不下第 1 件物品,因为它的重量为2


  • dp[1][2] = 1,此时容量为2的背包可以装下物品1,而它的价值为1


  • 第1行后面容量>1的背包,都可以装下物品1,因此它们的价值都为1


现在考虑的是装 2 件物品(i = 2)

  • dp[2][1] = 0,容量为1的背包即装不下物品1(重量2)也装不下物品2(重量3)


  • 容量为2的背包装不下物品2,但可以装下物品1
  • 因此dp[2][2] = dp[1][2] = 1


此时引出第一种情况:

  1. 当背包容量小于物品 i 的重量 w[i] 时,拿不了物品 i ,所以考虑拿 i - 1个物品的最大总价值。
  2. 得到状态转移方程: dp[i][j] = dp[ i - 1][j] ( j < w[i])
       if (j < w[i]) {dp[i][j] = dp[i - 1][j];}

  • 容量为3的背包既可以装下物品2、也可以装下物品1。要使价值最大,我们肯定会选装物品2,这样就是dp[2][3] = 3。
  • 此时的背包容量都用来装物品2,已经满了,装不下物品1(对应dp[1][0] = 0)。
  • 但这只是我们主观得出的选择,计算机怎么知道是选装物品1还是物品2呢?

此时引出第二种情况:

  1. 当背包容量大于物品 i 的重量 w[i] 时,可以拿物品 i ,此时需要考虑拿了物品 i 的价值大,还是不拿物品 i 的价值大
  2. 不拿物品 i ,就是第一种情况,考虑拿 i - 1个物品的最大总价值
  3. 拿物品 i ,就需要把物品 i 装入到背包(+c[i])。但!还需要考虑装完物品 i 的背包还可以装多少(+ dp[ i - 1][ j - w[i]],即上述的 dp[1][0])
  4. 得到状态转移方程: dp[i][j] = c[i] + dp[ i - 1][ j - w[i]] ( j >= w[i])

如下,容量为5的背包,两个物品都可以装下

所以dp[2][5] = c[2] + dp[1][5 - 3] 即 4 = 3 + 1

   if (j >= w[i]) {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + c[i]);}

综上,可以得到总的状态转移方程:

dp[i][j]=dp[i−1][j],(j<w[i])dp[i][j] = dp[ i - 1][j] ,( j < w[i]) dp[i][j]=dp[i−1][j],(j<w[i])
dp[i][j]=c[i]+dp[i−1][j−w[i]],(j>=w[i])dp[i][j] = c[i] + dp[ i - 1][ j - w[i]], ( j >= w[i]) dp[i][j]=c[i]+dp[i−1][j−w[i]],(j>=w[i])
状态转移方程代表了我们接下来的选择,根据它计算出表内的所有值,如下图

时间复杂度为O(n2),空间复杂度为O(M*n)


得到题目答案:12

完整代码

    // 二维dppublic static int dp_2d(int n, int[] w, int[] c, int M) {int[][] dp = new int[n + 1][M + 1];for (int i = 1; i < n + 1; ++i) {for (int j = 1; j < M + 1; ++j) {if (j < w[i]) {dp[i][j] = dp[i - 1][j];}else{dp[i][j] = Math.max(dp[i - 1][j], c[i] + dp[i - 1][j - w[i]]);}}}return dp[n][M];}

进阶

滚动数组

  • 由上述的填表顺序可以发现,每次 dp[i][j] 的值都是由 i - 1 行左上方或正上方的dp值得出。因此可以尝试把 [i] 这一维度去除,二维dp降成一维dp,通过不断刷新一维dp表的值,模拟上述二维dp的填表过程。时间复杂度仍为O(n^2^),空间复杂度降为O(M)。
  • 但会发现若按照从左往右的顺序填表,会把需要用到的值覆盖,固填表方向需要反过来。


完整代码

    // 二维降一维,滚动数组public static int dp_1d(int n, int[] w, int[] c, int M) {int[] dp = new int[M + 1];for (int i = 1; i < n + 1; ++i) {for (int j = 1; j < M + 1; ++j) {if (j >= w[i]) {dp[j] = Math.max(dp[j], dp[j - w[i]] + c[i]);}}}return dp[M];}

END

参考资料:
https://www.bilibili.com/video/BV1C7411K79wfrom=search&seid=9137630755457139754

Java入门算法(动态规划篇2:01背包精讲)相关推荐

  1. Java入门算法(动态规划篇1:初识动规)

    本专栏已参加蓄力计划,感谢读者支持❤ 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算 ...

  2. Java入门算法(贪心篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算法 ...

  3. Java入门算法(树篇)

    本专栏已参加蓄力计划,感谢读者支持❤ 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算 ...

  4. Java入门算法(滑动窗口篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持❤ 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算 ...

  5. Java入门算法(暴力篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算法 ...

  6. Java入门算法(排序篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算法 ...

  7. Java入门算法(递归篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算法 ...

  8. Java入门算法(双指针篇)丨蓄力计划

    本专栏已参加蓄力计划,感谢读者支持 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算法 ...

  9. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

最新文章

  1. python笔试题奥特曼打怪兽_python笔试做错的题目
  2. centos 7 安装 Vue
  3. python在会计工作中的应用-python有什么用(会计专业)
  4. ignite自定义函数
  5. asp程序错误详细说明例表
  6. 新手入门深度学习 | 目录
  7. 神策数据荣登《中国企业家》“未来之星”榜单
  8. AR/QCA SPI 启动原理和 ART 地址定位原理
  9. 流式细胞凋亡检测实验常见问题解析
  10. maven工程xml文件路径问题
  11. 请教怎么查询ORACLE的历史操作记录!
  12. [插头DP自我总结]
  13. 机器学习之支持向量机算法(一)
  14. JAVA基础之列表(list)和字典(dict)
  15. 18个黑科技网站,你想要的软件都能找到,请收藏!!!
  16. 电子发票对报销类saas的影响
  17. 为什么linux中权限r对应4、w对应2、x对应1
  18. 关于Vue.js和React.js,听听国外的开发者怎么说?
  19. 行远自迩,乘势前行 | 菊风再中标中原银行远程银行项目
  20. 英语口语练习四十二之12种“安静”的表达

热门文章

  1. 数值分析(part1)--拉格朗日插值
  2. Django(part32)--一对一映射
  3. 混合高斯模型_EM算法求解高斯混合模型(GMM)
  4. SAP UI5 初学者教程之十二 - 使用 CSS 类对 UI 进行进一步美化试读版
  5. 如何访问 SAP Screen Personas 培训系统以及完成一个最简单的例子
  6. SAP Fiori SSL 和 SAML 2.0 配置文档
  7. SAP Fiori Elements 公开课第二单元视频的台词和课程主要内容
  8. 如何自行查询SAP Spartacus Organization Unit List的取数逻辑
  9. 关于jasmine里debugElement.query和fixture.detectChanges的依赖关系
  10. SAP Spartacus ConfigModule.withConfig的处理逻辑