当前位置:   article > 正文

使用二维动态规划解决背包问题

二维动态规划

一、01 背包概述:

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

二、二维dp数组01背包的解法:

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

    对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示为从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少

2.如何推导dp[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得到的最大价值
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

3.如何初始化p[i][j]?

  由图可知dp[i - 1][j]dp[i][j]的上方, dp[i - 1][j - weight[i]]dp[i][j]的左上方,所以只需要确定前面的值即可推出dp[i][j]。

假设题目要求为:

首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。如图:

 

在看其他情况。

状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。

dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。

那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。

当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。

此时dp数组初始化情况如图所示:

 

dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?

其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。

初始-1,初始-2,初始100,都可以!

但只不过一开始就统一把dp数组统一初始为0,更方便一些。

如图:

所以总的初始化为:

  1. // dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
  2. //定义dp[i][j]二维数组 初始化i长度物品数量 j长度为背包容量加1
  3. vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
  4. // j < weight[0]已在上方被初始化为0
  5. // j >= weight[0]的值就初始化为value[0]
  6. for (int j = weight[0]; j <= bagweight; j++) {
  7. dp[0][j] = value[0];
  8. }

三、完整代码为:

  1. //二维dp数组实现
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. void solve(int n,int bagweight) {
  5. vector<int> weight(n, 0); // 存储每件物品所占空间
  6. vector<int> value(n, 0); // 存储每件物品价值
  7. vector<int> isLoad(n, 0);
  8. //初始物品重量和物品价值
  9. for(int i = 0; i < n; ++i) {
  10. cout<<"请分别输入第"<<i+1<<"个物品的重量和价值:"<<endl;
  11. cin >> weight[i];
  12. cin >> value[i];
  13. }
  14. // dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
  15. //定义dp[i][j]二维数组 初始化i长度物品数量 j长度为背包容量加1
  16. vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
  17. //dp[i][j] = max{dp[i-1][j],dp[i-1][j-weight[i]]+value[i]} = 最大值{不装i物品,装i物品}
  18. // 初始化, 因为需要用到dp[i - 1]的值
  19. // j < weight[0]已在上方被初始化为0
  20. // j >= weight[0]的值就初始化为value[0]
  21. for (int j = weight[0]; j <= bagweight; j++) {
  22. dp[0][j] = value[0];
  23. }
  24. for(int i = 1; i < weight.size(); i++) { // 遍历物品
  25. for(int j = 0; j <= bagweight; j++) { // 遍历容量
  26. // 如果装不下这个物品,那么就继承dp[i - 1][j]的值
  27. if (j < weight[i]) dp[i][j] = dp[i - 1][j];
  28. // 如果能装下,就将值更新为 不装这个物品的最大值 和 装这个物品的最大值 中的 最大值
  29. // 装这个物品的最大值由容量为j - weight[i]的包任意放入序号为[0, i - 1]的最大值 + 该物品的价值构成
  30. else
  31. {
  32. if(dp[i - 1][j]>dp[i - 1][j - weight[i]] + value[i])
  33. {
  34. dp[i][j] = dp[i - 1][j];
  35. isLoad[i] = 0;
  36. }
  37. else
  38. {
  39. dp[i][j] = dp[i - 1][j - weight[i]] + value[i];
  40. isLoad[i] = 1;
  41. }
  42. }
  43. }
  44. }
  45. //输出物品为个时 背包容量bagweight 的最大价值
  46. cout << "最大价值为:"<<dp[weight.size() - 1][bagweight] << endl;
  47. for(int i=0 ; i<n;i++)
  48. {
  49. if(isLoad[i]==0)
  50. cout<<"第"<<i+1<<"个物品没装"<<endl;
  51. else
  52. cout<<"第"<<i+1<<"个物品装了"<<endl;
  53. }
  54. }
  55. int main() {
  56. int n,bagweight;// bagweight代表背包的空间
  57. while(1) {
  58. cout<<"请输入物品数量和背包容量:"<<endl;
  59. cin >> n >> bagweight;
  60. solve( n, bagweight);
  61. }
  62. return 0;
  63. }

四、时间复杂度:

   二维动态规划的时间复杂度为O(n*bagweight),其中n是物品的数量,bagweight是背包的容量限制。在动态规划的过程中,需要填充一个二维的dp数组,该数组的大小为(n+1) * (bagweight+1)。每个位置的值需要通过比较和计算得到,因此总共需要进行(n+1) * (bagweight+1)次操作。 由于常数项在复杂度分析中不影响结果的趋势,我们可以简化表示为O(n*bagweight)。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/466244
推荐阅读
相关标签
  

闽ICP备14008679号