【动态规划】三种背包问题(01背包、完全背包、多重背包)

本文转载自:https://blog.csdn.net/sinat_30973431/article/details/85119871

一、01背包

问题描述:给定n个物体(它们的重量为:w1,w2,......,wn,价值为:v1,v2,......,vn) 和 一个承受重量为W的背包,问怎么选取这些物体,放在背包中(不超过背包的承重),让所取的子集达到最大价值。

1、基本实现

首先,我们很自然想到穷举法,只要给出n个物体的所有组合(子集),分别各个子集的总价值,去掉那些总重量超过背包承重W的子集之后,对剩下的子集中找到总价值最大的那一个,就是我们想要的结果了。

但是,由于n各物体的有2^n个子集,所以上面的做法的时间复杂度将是O(2^n),除非n很小,否则时间性能消耗非常严重。

我们换一种思路,如果物体有n-1个,在背包容量为0,1,2,......,W各个情况下所能得到的最大价值都已经知道,那么当我们多考虑一个物体,即物体有n个时,就可以分为两个情况进行考虑:(1)第n个物体不放进背包中;(2)第n个物体放进背包。

对于第一种情况,考虑n个物体,跟考虑n-1个物体没有区别,所以考虑n个物体的情况下,在一定承重量的背包中所能得到的最大价值等于只考虑n-1个物体在同等承重量的背包下所能得到的最大价值。

对于第二种情况,则最大价值 = 物体n的价值vn + 背包在剩余空间(W-wn)下只考虑n-1个物体所能达到的最大价值。

如下面递推式所示,

其中,F(i,j)表示前 i 个物体(1≤ i ≤n)在背包承重为 j 时,所能达到的最大价值。如果把它看成一个状态的话,那么也就是说,状态F(i,j)的值等于状态F(i-1,j)、状态F(i-1,j-wi)与vi之和 两者中的最大值。那么要求 i 个物体在一定承重背包中可取的最大价值,只需考虑 i-1 个物体在不同承重量(0,1, 2, ......,W)的背包下可取的最大价值。类似地,要想知道 i-1 个物体在一定承重的背包中可取的最大价值,只需知道 i-2 个物体在不同承重量的背包中可取的最大价值。以此类推,直到所考虑的物体个数变为 1 。

所以,只要我们知道物体个数为 1 时,在不同承重量的背包中所能取到的最大价值,就可以依次求物体个数为2,3,......,n的情况下在不同承重量的背包中所能取的最大价值。

初始条件为:i = 1     当 j ≥ w1时,F(1,j) = v1; 当 0≤ j <w1时,F(1,j) = 0.

 知道了上面的递推式、初始条件,就可以写出01背包的代码了。


 
 
  1. // 二维实现
  2. public class PkgTest {
  3. public static void main(String[] args) {
  4. //w[]:物品重量,v[]:物品价值,m:背包承重,n:物品个数,maxValue[][]:状态
  5. int[] w = { 0, 10, 3, 4, 5}; //第一个数为0,是为了让输出时,i表示有i个物品
  6. int[] v = { 0, 3, 4, 6, 7};
  7. int m = 10;
  8. int n = 4;
  9. int[][] maxValue = new int[ 5][ 16];
  10. // 01背包算法
  11. for ( int i= 1; i<=n; i++) { //第一个物体 是 第1行
  12. for ( int j= 0; j<=m; j++) {
  13. if (i > 0) {
  14. maxValue[i][j] = maxValue[i- 1][j];
  15. if (j >= w[i]) {
  16. maxValue[i][j] = max(maxValue[i][j], maxValue[i- 1][j-w[i]] + v[i]);
  17. //注:maxValue[i][j]其实就是maxValue[i-1][j] 因为上面的赋值
  18. }
  19. } else { //初始化,只考虑一个物体
  20. if (j >= w[ 1]) {
  21. maxValue[ 1][j] = v[ 1];
  22. }
  23. }
  24. }
  25. }
  26. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
  27. System.out.println();
  28. // 打印背包的不同承重量
  29. System.out.print( " " + "\t");
  30. for ( int i= 0; i<=m; i++) {
  31. System.out.print(i + "\t");
  32. }
  33. System.out.println();
  34. // 打印01背包算法 得到的状态矩阵值
  35. for ( int i= 1; i<=n; i++) {
  36. System.out.print( "i="+ i + "\t");
  37. for ( int j= 0; j<=m; j++) {
  38. System.out.print(maxValue[i][j]+ "\t");
  39. }
  40. System.out.println();
  41. }
  42. }
  43. public static int max(int a, int b) {
  44. if (a > b) {
  45. return a;
  46. }
  47. return b;
  48. }
  49. }

结果如下图所示,

2、滚动数组实现

上面的基本实现时间复杂度为O(nW),空间复杂度为O(nW),事实上空间可以做优化。我们可以看到,第 i 层的状态至于第 i-1 层的状态有关,与第 i-2 层的状态没有直接关系,如下面的图所示,除了第一层的初始条件,其他每一层的状态值的求解依赖于上一层。

所以,我们可以用只有两行的二维数组 maxValue[2][ ] 来存储,如下面代码所示。


 
 
  1. // 滚动数组实现
  2. public class PkgTest {
  3. public static void main(String[] args) {
  4. int[] w = { 0, 10, 3, 4, 5};
  5. int[] v = { 0, 3, 4, 6, 7};
  6. int m = 10;
  7. int n = 4;
  8. int k = 0; // k的作用是指向数组的某一行(两行其中之一),不能再用下标i来指定数组的行数了
  9. int[][] maxValue = new int[ 2][ 16];
  10. for ( int i= 1; i<=n; i++) {
  11. for ( int j= 0; j<=m; j++) {
  12. if (i > 1) {
  13. k = i & 1; // k = i % 2 获得滚动数组当前索引 k
  14. maxValue[k][j] = maxValue[k^ 1][j]; // k ^ 1 获得滚动数组逻辑上的“上一行”
  15. if (j >= w[i]) {
  16. maxValue[k][j] = max(maxValue[k][j], maxValue[k^ 1][j-w[i]] + v[i]);
  17. }
  18. } else {
  19. if (j >= w[ 1]) {
  20. maxValue[ 1][j] = v[ 0];
  21. }
  22. }
  23. }
  24. }
  25. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[k][m]);
  26. System.out.println();
  27. System.out.print( "i=0"+ "\t");
  28. for ( int i= 0; i<=m; i++) {
  29. System.out.print(maxValue[ 1][i] + "\t");
  30. }
  31. System.out.print( "\ni=1"+ "\t");
  32. for ( int i= 0; i<=m; i++) {
  33. System.out.print(maxValue[ 0][i] + "\t");
  34. }
  35. }
  36. public static int max(int a, int b) {
  37. if (a > b) {
  38. return a;
  39. }
  40. return b;
  41. }
  42. }

需要注意的是,代码中我们不再用下标 i 指向数组的索引,而是用 k 指向数组,k=0、k=1表示相邻的两行状态值,k=1不一定是k=0逻辑上的上一行,需要看具体情况(这个把整个过程画一下就知道了)。

初始化时,我们在标号为1的一行输入状态值。

当 i = 2 大于1时,

由于       

此时 k = 0指向数组的另一行,我们通过 k ^ 1找到 k 逻辑上的“上一行”,并通过“上一行”算出本行中的状态值

以此类推,不断算出并刷新各行的状态值,直到最后两行。

运行结果如下所示,

3、一维数组实现

01背包还可以用一维数组实现,只不过此时的递推式 & 初始条件就需要做些改变了。要想用一维数组存放所有状态,也就是让该数组某个时间是第 i-1 层的状态,而过一段时间之后则成为第 i 层的状态。如下面所演示的,初始状态下,一维数组 maxValue[ ]存放的是 i = 1 时的状态值(对应上面的F[ 1 ][ j ],j = 0,1,2,......,W)

而当 i = 2 时,我们就需要计算 第二行的状态值,并把它们覆盖到maxValue[ ]一维数组之上。

问题是怎么覆盖呢?如果我们还是跟二维数组一样从前往后遍历数组,覆盖的过程中某一时刻如下图所示,其中数组前面部分是属于 i=2 层的状态值,后面部分属于 i=1 层的状态值。

但是,当我们继续计算并写入 i=2 的状态值时,很有可能用到 i=1 的某个状态值,而这个状态值却已经被覆盖掉了,比如,我们计算 i=2 的maxValue[ 6 ]时,要找到 i=1 的 maxValue[ 3 ] 状态值,本来它应该为0,但却变成4,如果我们用4去计算

maxValue[ 6 ],就会得到错误的结果。

事实上,我们覆盖的过程中,应该采用从后到前的顺序遍历。首先,改写maxValue[ W ]的值。

改写之后,原来maxValue[ W ]的值就由3变为4。接着,改写maxValue[ W-1 ],由于计算 i=2 的maxValue[ W-1 ] 不需要用到 i=1 的maxValue[ W ]状态,所以,maxValue[ W ]的改动不影响maxValue[ W-1 ]的计算。

以此类推,就可以在原来的数组上面不断覆盖最新一层的状态值了。

上面的过程的递推式为:

初始条件为:i = 1     当 v≥ w1时,F(v) = v1; 当 0≤ j<w1时,F(v) = 0.

代码实现如下,


 
 
  1. // 一维数组
  2. public class PkgTest {
  3. public static void main(String[] args) {
  4. int[] w = { 0, 10, 3, 4, 5};
  5. int[] v = { 0, 3, 4, 6, 7};
  6. int m = 10;
  7. int n = 4;
  8. //int k = 0;
  9. int[] maxValue = new int[ 16];
  10. for ( int i= 1; i<=n; i++) {
  11. for ( int j=m; j>=w[i]; j--) {
  12. maxValue[j] = max(maxValue[j], maxValue[j-w[i]] + v[i]);
  13. }
  14. //验证 结果和二维实现的输出结果完全一样
  15. //for (int k=0; k<=m; k++) {
  16. // System.out.print(maxValue[k] + "\t");
  17. //}
  18. //System.out.println();
  19. }
  20. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[m]);
  21. System.out.println();
  22. for ( int i= 0; i<=m; i++) {
  23. System.out.print(maxValue[i] + "\t");
  24. }
  25. }
  26. public static int max(int a, int b) {
  27. if (a > b) {
  28. return a;
  29. }
  30. return b;
  31. }
  32. }

运行结果如下所示,

4、小结

尽管滚动数组、一维数组能省一些空间,但是,这两种做法比较适合只求最大价值的需求。当需要输出最佳方案时,我们常常要回溯历史信息,这时,一般就只能用二维数组这种保存有各个状态值的方法了。

这里再稍微讲下滚动数组。事实上,我们可以在一些其它算法看到滚动数组的思想。比如,在斐波那契数列中,我们一般把各个斐波那契数存在一个数组中。但如果我们只需要打印一遍斐波那契数列,或者只需要计算某个斐波那契数时,我们可以只用三个变量(或者三个空间大小的数组),用前两个变量存放初始斐波那契数,然后两者相加之和放在第三个变量。不断地滚动下去,直到求得所需要的斐波那契数。

再比如,二叉树删除一个结点。我们通常让两个引用(或指针)一个指向某个结点,一个指向该节点的父节点。两个引用不断往树深处滚动,直到指向子节点的引用找到待删除节点。这时,我们就可以利用指向父节点的引用对其进行删除了。

二、完全背包

问题描述:完全背包是在01背包的基础上加了个条件——这n种物品都有无限的数量可以取,问怎样拿才可以实现价值最大化。

1、基本实现

虽然题意中每种有无限件,但这里有个隐藏条件:背包承重量的固定性导致每种最多只能取某个值,再多就放不下了,这个值就是W / wi。也就是说,对于第 i 种物品,它可以取0,1,2,......,W / wi(向下取整)件。而在01背包中,对于第 i 种物品,只能取0,1件。我们可以看到,01背包其实就是完全背包的一个特例。所以我们可以用类似01背包的思路写出完全背包的基本算法。

前面给出的01背包的状态转移方程也可以写成这种形式:

下面是基本实现的代码,


 
 
  1. // 完全背包的基本实现
  2. public class CompleteTest {
  3. public static void main(String[] args) {
  4. int[] w = { 0, 10, 3, 4, 5};
  5. int[] v = { 0, 3, 4, 6, 7};
  6. int m = 10;
  7. int n = 4;
  8. int[][] maxValue = new int[ 5][ 16];
  9. for ( int i= 1; i<=n; i++) {
  10. for ( int j= 0; j<=m; j++) {
  11. if (i > 1) {
  12. maxValue[i][j] = maxValue[i- 1][j];
  13. //if (j >= v[i]) {
  14. // maxValue[i][j] = max(maxValue[i][j], maxValue[i-1][j-v[i]] + w[i]);
  15. //}
  16. if (j/w[i] >= 1) {
  17. int maxTmp = 0;
  18. // 对于i个物品,进行j/w[i]次比较得到最大值;而01背包中只需要进行1次比较
  19. for ( int k= 1; k<=j/w[i]; k++) {
  20. if (maxValue[i- 1][j-k*w[i]] + k*v[i] > maxTmp) {
  21. maxTmp = maxValue[i- 1][j-k*w[i]] + k*v[i];
  22. }
  23. }
  24. maxValue[i][j] = max(maxValue[i][j], maxTmp);
  25. }
  26. } else {
  27. //if (j >= v[0]) {
  28. // maxValue[0][j] = w[0];
  29. //}
  30. if (j/w[ 1] >= 1) {
  31. maxValue[ 1][j] = j/w[ 1] * v[ 1];
  32. }
  33. }
  34. }
  35. }
  36. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
  37. System.out.println();
  38. // 打印背包的不同承重量
  39. System.out.print( " " + "\t");
  40. for ( int i= 0; i<=m; i++) {
  41. System.out.print(i + "\t");
  42. }
  43. System.out.println();
  44. // 打印01背包算法 得到的状态矩阵值
  45. for ( int i= 1; i<=n; i++) {
  46. System.out.print( "i="+ i + "\t");
  47. for ( int j= 0; j<=m; j++) {
  48. System.out.print(maxValue[i][j]+ "\t");
  49. }
  50. System.out.println();
  51. }
  52. }
  53. public static int max(int a, int b) {
  54. if (a > b) {
  55. return a;
  56. }
  57. return b;
  58. }
  59. }

代码跟01背包的二维数组实现几乎一模一样,我们只需在01背包基本实现的代码中注释掉两段代码,再略微修改就变成完全背包的了。

运行结果如图所示,结果和01背包不同。

2、时间优化

基本实现中的时间复杂度为O(nW\sum W/wi) = O(nW*max(W / wi))

 

3、空间优化

基本实现采用的是二维数组,事实上我们也可以用一维数组实现。完全背包的一维数组实现和01背包也是几乎完全相同,唯一差别是完全背包的内循环是正向遍历,而01背包的内循环是逆向遍历。


 
 
  1. // 完全背包的一维数组实现
  2. public class CompleteTest2 {
  3. public static void main(String[] args) {
  4. int[] w = { 0, 10, 3, 4, 5};
  5. int[] v = { 0, 3, 4, 6, 7};
  6. int m = 10;
  7. int n = 4;
  8. int[] maxValue = new int[ 16];
  9. for ( int i= 1; i<=n; i++) {
  10. //for (int j=m; j>=w[i]; j--) {
  11. // 正序遍历; 01背包是逆序遍历
  12. for ( int j=w[i]; j<=m; j++) {
  13. maxValue[j] = max(maxValue[j], maxValue[j-w[i]] + v[i]);
  14. }
  15. //验证 结果和二维实现的输出结果完全一样
  16. //for (int k=0; k<=m; k++) {
  17. // System.out.print(maxValue[k] + "\t");
  18. //}
  19. //System.out.println();
  20. }
  21. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[m]);
  22. System.out.println();
  23. for ( int i= 0; i<=m; i++) {
  24. System.out.print(maxValue[i] + "\t");
  25. }
  26. }
  27. public static int max(int a, int b) {
  28. if (a > b) {
  29. return a;
  30. }
  31. return b;
  32. }
  33. }

运行结果如下,

三、多重背包

问题描述:多重背包是在01背包的基础上,加了个条件:第 i 件物品有ni件。

1、基本实现

我们考虑一下,如果所有ni都满足ni ≥ W / wi,那不就变成完全背包的问题了么。可见,完全背包的基本实现思路也可以应用到多重背包的基本实现。对于多重背包的基本实现,与完全背包是基本一样的,不同就在于物品的个数上界不再是v/c[i]而是n[i]与v/c[i]中较小的那个。所以我们要在完全背包的基本实现之上,再考虑这个上界问题。

代码实现如下所示,代码与完全背包的区别除了多了个表示物品个数的数组n[ ]之外,只在内循环的控制条件那里。


 
 
  1. // 多重背包的基本实现
  2. public class MultipleTest {
  3. public static void main(String[] args) {
  4. int[] w = { 0, 10, 3, 4, 5};
  5. int[] v = { 0, 3, 4, 6, 7};
  6. //第i个物品对应的个数
  7. int[] mount = { 0, 5, 1, 2, 1};
  8. int m = 10;
  9. int n = 4;
  10. int[][] maxValue = new int[ 5][ 16];
  11. for ( int i= 1; i<=n; i++) {
  12. for ( int j= 0; j<=m; j++) {
  13. if (i > 1) {
  14. maxValue[i][j] = maxValue[i- 1][j];
  15. if (j/w[i] >= 1) {
  16. int maxTmp = 0;
  17. //for (int k=1; k<=j/w[i]; k++) {
  18. //多重背包与完全背包的区别只在内循环这里
  19. for ( int k= 1; k<=j/w[i] && k<=mount[i]; k++) {
  20. if (maxValue[i- 1][j-k*w[i]] + k*v[i] > maxTmp) {
  21. maxTmp = maxValue[i- 1][j-k*w[i]] + k*v[i];
  22. }
  23. }
  24. maxValue[i][j] = max(maxValue[i][j], maxTmp);
  25. }
  26. } else {
  27. if (j/w[ 1] >= 1) {
  28. maxValue[ 1][j] = j/w[ 1] * v[ 1];
  29. }
  30. }
  31. }
  32. }
  33. System.out.println( "4个物品在背包承重为10的情况下的组合的最大价值为:"+maxValue[n][m]);
  34. System.out.println();
  35. // 打印背包的不同承重量
  36. System.out.print( " " + "\t");
  37. for ( int i= 0; i<=m; i++) {
  38. System.out.print(i + "\t");
  39. }
  40. System.out.println();
  41. // 打印01背包算法 得到的状态矩阵值
  42. for ( int i= 1; i<=n; i++) {
  43. System.out.print( "i="+ i + "\t");
  44. for ( int j= 0; j<=m; j++) {
  45. System.out.print(maxValue[i][j]+ "\t");
  46. }
  47. System.out.println();
  48. }
  49. }
  50. public static int max(int a, int b) {
  51. if (a > b) {
  52. return a;
  53. }
  54. return b;
  55. }
  56. }

运行结果如下所示,

2、时间优化(通过二进制拆分转化为01背包问题)

待续

3、优先队列实现

待续

四、小结

本质上,完全背包是多重背包的一个特例:当n[i]都大于等于 V / c[i]  时,多重背包就变为完全背包问题了;01背包是完全背包的一个特例:当第i种物品由可以取0,1,2,...件变为只能取0,1件时(也就是从V / c[i] + 1 种状态 变为 2种状态),完全背包就变为01背包问题了。三者两两之间的关系有点像集合之间的包含关系。

 

  •                     <li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#csdnc-thumbsup"></use>
                        </svg><span class="name">点赞</span>
                        <span class="count">10</span>
                        </a></li>
                        <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-Collection-G"></use>
                        </svg><span class="name">收藏</span></a></li>
                        <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-fenxiang"></use>
                        </svg>分享</a></li>
                        <!--打赏开始-->
                                                <!--打赏结束-->
                                                <li class="tool-item tool-more">
                            <a>
                            <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                            </a>
                            <ul class="more-box">
                                <li class="item"><a class="article-report">文章举报</a></li>
                            </ul>
                        </li>
                                            </ul>
                </div>
                            </div>
            <div class="person-messagebox">
                <div class="left-message"><a href="https://blog.csdn.net/sinat_30973431">
                    <img src="https://profile.csdnimg.cn/F/B/3/3_sinat_30973431" class="avatar_pic" username="sinat_30973431">
                                            <img src="https://g.csdnimg.cn/static/user-reg-year/1x/4.png" class="user-years">
                                    </a></div>
                <div class="middle-message">
                                        <div class="title"><span class="tit"><a href="https://blog.csdn.net/sinat_30973431" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">JeremyChan1887</a></span>
                                            </div>
                    <div class="text"><span>发布了73 篇原创文章</span> · <span>获赞 30</span> · <span>访问量 3万+</span></div>
                </div>
                                <div class="right-message">
                                            <a href="https://im.csdn.net/im/main.html?userName=sinat_30973431" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
                        </a>
                                                            <a class="btn btn-sm  bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">关注</a>
                                    </div>
                            </div>
                    </div>
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多重背包问题是指在一定的背包容量下,有多个物品的重量和价值,每个物品有一定的数量限制,要求在放入背包时,使得背包中物品的总价值最大化。动态规划是解决多重背包问题的一种常见方法。 其中,引用提供了一种将多重背包问题转化为01背包问题的方法。这种方法将每个多重背包问题的物品按照其数量进行拆分,使其变成多个01背包问题。然后使用动态规划的思想,依次求解每个01背包问题,最终得到多重背包问题的最优解。 引用提供了一种对完全背包问题的暴力解法做简单修改的方法。该方法同样使用动态规划的思想,对每个物品进行遍历,并在背包承重为j的前提下,求解每个物品能放入的最大数量。然后根据物品的重量和价值进行计算,得到多重背包问题的最优解。 这两种方法都可以用来解决多重背包问题,并根据实际情况选择合适的方法进行求解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [动态规划(五):多重背包问题](https://blog.csdn.net/qq_42138662/article/details/118434151)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [动态规划:多重背包问题](https://blog.csdn.net/qq_42174306/article/details/124345411)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [动态规划4:多重背包](https://blog.csdn.net/qq_40772692/article/details/81435230)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值