背包问题-01背包,完全背包,多重背包

太过爱你忘了你带给我的痛 2022-05-14 09:23 420阅读 0赞
  • 背包问题-01背包,完全背包,多重背包


  • 01背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,每种物品只有一件,每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

假设目前已经有 i-1件物品装在容量为 j 的背包中,并且得到最大价值Package[ i-1 ][ j ],当前装第i件,那么讨论分两个角度,如果第i个物品装不入背包,那么最大价值不变。如果第i个物品可以放入,那么如果背包容量为 j-Volume[i]的时候的最大装包价值加上第i个物品价值大于Package[i-1][j],那么Package[i][j]最大价值就更新了,否则就不装i

得到状态转移方程:

a. Volume[ i ] > j :Package[ i-1 ][ j ]

b. j >= Volume[ i ] :max( Package[ i-1 ][ j-Volume[i] ] +Value[ i ], Package[ i-1 ][ j ] )

实现代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Volume[MAX_SIZE]; /*每个物品所占空间(或重量)*/
  7. int Value[MAX_SIZE]; /*物品价值*/
  8. int Package[MAX_SIZE][MAX_SIZE]; /*未优化,Package[i][j] 表示前i个物品放入容量为j的购物车的最大价值*/
  9. int MAX_Volume, Goods_Num; /*最大容量,物品数量*/
  10. void Init()
  11. {
  12. memset(Package, 0, sizeof(Package));
  13. }
  14. void _01Package()
  15. {
  16. for (int i = 1; i <= Goods_Num; i++)
  17. {
  18. for (int j = 1; j <= MAX_Volume; j++)
  19. {
  20. if (j < Volume[i]) /*装不下第i个物品*/
  21. Package[i][j] = Package[i - 1][j];
  22. else
  23. Package[i][j] = max(Package[i - 1][j], Package[i - 1][j - Volume[i]] + Value[i]);
  24. /*将第i个物品放在容量为j - Volume[i](已得的最优情况)的购物车里*/
  25. }
  26. }
  27. }
  28. void Print_Res() /*打印结果*/
  29. {
  30. cout << "The max value of Package:" << Package[Goods_Num][MAX_Volume] << endl;
  31. int i, j = MAX_Volume;
  32. cout << "Goods in Package: ";
  33. for (int i = Goods_Num; i > 0; i--)
  34. {
  35. if (Package[i][j] > Package[i - 1][j])
  36. {
  37. cout << i<<" ";
  38. j -= Volume[i];
  39. }
  40. }
  41. cout << endl;
  42. }

优化:

在设计算法时会发现,上一层的数据在本层中并没有修改,而是作为状态的记录,而且本层当前Index只会用到上一层Index之前的数据,所以可以降成一维,称为滚动数组。并且要求每次都重后往前遍历,避免重复叠包

优化代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Volume[MAX_SIZE]; /*每个物品所占空间(或重量)*/
  7. int Value[MAX_SIZE]; /*物品价值*/
  8. int _Package[MAX_SIZE]; /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
  9. int MAX_Volume, Goods_Num; /*最大容量,物品数量*/
  10. void Init()
  11. {
  12. memset(_Package, 0, sizeof(_Package));
  13. }
  14. void _01Package_Optimize() /*优化算法*/
  15. {
  16. for (int i = 1; i <= Goods_Num; i++)
  17. for (int j = MAX_Volume; j >= Volume[i]; j--) /*每个物品最多只能装一次,从后开始遍历防止最优情况叠加*/
  18. _Package[j] = max(_Package[j], _Package[j - Volume[i]] + Value[i]);
  19. }
  20. void Print_Res_Optimize()
  21. {
  22. cout << "The max value of Package:" << _Package[MAX_Volume] << endl;
  23. }

  • 完全背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,每种物品数量无限,每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

与01背包的思路基本相似,不过重要的一点是,每种物品是无限的,所以背包可以多填

得状态转移方程:

a. Volume[ i ] > jPackage[i-1][ j ]

b. j>=Volume[ i ] :max( Package[ i-1 ][ j-m\Volume[ i ] ] +m*Value[ i ], Package[ i-1 ][ j ] )*

实现代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Volume[MAX_SIZE]; /*每个物品所占空间(或重量)*/
  7. int Value[MAX_SIZE]; /*物品价值*/
  8. int Package[MAX_SIZE][MAX_SIZE]; /*未优化,Package[i][j] 表示前i个物品放入容量为j的购物车的最大价值*/
  9. int MAX_Volume, Goods_Num; /*最大容量,物品数量*/
  10. void Init()
  11. {
  12. memset(Package, 0, sizeof(Package));
  13. }
  14. void Total_Package()
  15. {
  16. for (int i = 1; i <= Goods_Num; i++)
  17. {
  18. for (int j = MAX_Volume; j >= Volume[i]; j--) /*重后往前避免单物品多填影响后序的填包,后填不影响前*/
  19. {
  20. int k = j / Volume[i]; /*最多再填k个i物品*/
  21. for (int m = 0; m <= k; m++)
  22. Package[i][j] = max(Package[i - 1][j - m * Volume[i]] + m * Value[i], Package[i - 1][j]);
  23. }
  24. }
  25. }

优化代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Volume[MAX_SIZE]; /*每个物品所占空间(或重量)*/
  7. int Value[MAX_SIZE]; /*物品价值*/
  8. int _Package[MAX_SIZE]; /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
  9. int MAX_Volume, Goods_Num; /*最大容量,物品数量*/
  10. void Init()
  11. {
  12. memset(_Package, 0, sizeof(_Package));
  13. }
  14. void Total_Package_Optimize() /*优化算法*/
  15. {
  16. for (int i = 1; i <= Goods_Num; i++)
  17. {
  18. for (int j = Volume[i]; j <= MAX_Volume; j++)/*一维,从前往后dp才能实现一个物品的多填*/
  19. _Package[j] = max(_Package[j], _Package[j - Volume[i]] + Value[i]);
  20. }
  21. }
  22. void Print_Res_Optimize()/*打印*/
  23. {
  24. cout << "The max value of Package:" << _Package[MAX_Volume] << endl;
  25. }

  • 多重背包:

概念:

有Goods_Num件物品,MAX_Volume的最大装载量,第i件物品有数量为Num[i],每种物品都有对应的重量或者说体积Volume[i],价值Value[i],求解装包的最大价值

状态转移方程推导:

其实就是在一个展开的01背包问题,比如有3件相同物品,不如将它展开成 1 1 1 形式,然后01装包

状态转移方程:

a. Volume[ i ] > j :Package[ i-1 ][ j ]

b. j >= Volume[i] :max( Package[ i-1 ][ j-Volume[ i ] ] +Value[i], Package[ i-1 ][ j ] )

只不过,对于物品数大于1的,需要展开Num[i]次

实现代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Volume[MAX_SIZE]; /*每个物品所占空间(或重量)*/
  7. int Value[MAX_SIZE]; /*物品价值*/
  8. int Num[MAX_SIZE]; /*i物品的个数*/
  9. int Package[MAX_SIZE]; /*优化后,Package[i]表示容量i的购物车所获得的最大价值*/
  10. int MAX_Volume, Goods_Num; /*最大容量,物品数量*/
  11. void Init()
  12. {
  13. memset(Package, 0, sizeof(Package));
  14. }
  15. void Multi_Package()
  16. {
  17. for (int i = 1; i <= Goods_Num; i++)
  18. {
  19. for (int j = 1; j <= Num[i]; j++)
  20. {
  21. for (int k = MAX_Volume; k >= Volume[i]; k--)
  22. Package[k] = max(Package[k], Package[k - Volume[i]] + Value[i]);
  23. }
  24. }
  25. }
  26. void Print_Res()
  27. {
  28. cout << "The max value of Package:" << Package[MAX_Volume] << endl;
  29. }

优化:

这时候想一个问题,如果一件物品的数量很大很大,那么,对于第三个循环需要跑的时间就很长,在前面说过,多重背包是可拆成01背包形式,那么,何不提前处理 Volume[MAX_SIZE],Value[MAX_SIZE],将问题先转成01背包。

转化时有技巧,运用快速幂的知识,将一件物品拆成 1,2,4…….,当然有个容易想的前提:1~N的数可由2的若干指数和表示

优化代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<memory.h>
  4. using namespace std;
  5. #define MAX_SIZE 1024
  6. int Op_Vol[MAX_SIZE]; /*优化后的物品体积*/
  7. int Op_Val[MAX_SIZE]; /*优化后的物品价值*/
  8. int Package[MAX_SIZE]; /*背包*/
  9. int MAX_Volume,GoodsNum; /*背包最大体积,原先的货品数量*/
  10. int Op_GoodsNum; /*优化后的物品数*/
  11. void Init() /*初始化*/
  12. {
  13. Op_GoodsNum=0;
  14. memset(Op_Val,0,sizeof(Op_Val));
  15. memset(Op_Vol,0,sizeof(Op_Vol));
  16. memset(Package,0,sizeof(Package));
  17. }
  18. void Multi_Package()
  19. {
  20. for(int i=1;i<=Op_GoodsNum;i++) /*优化后转变为01背包问题*/
  21. {
  22. for(int j=MAX_Volume;j>=Op_Vol[j];j--)
  23. Package[j]=max(Package[j],Package[j-Op_Vol[i]]+Op_Val[i]);
  24. }
  25. }
  26. int main()
  27. {
  28. int vol,val,num;
  29. while(cin>>GoodsNum>>MAX_Volume)
  30. {
  31. Init();
  32. for(int i=1;i<=GoodsNum;i++)
  33. {
  34. cin>>vol>>val>>num;
  35. for(int j=1;j<=num;j<<=1) /*优化成01背包问题*/
  36. {
  37. Op_Val[++Op_GoodsNum]=j*val;
  38. Op_Vol[Op_GoodsNum]=j*vol;
  39. num-=j;
  40. }
  41. if(num>0)
  42. {
  43. Op_Val[++Op_GoodsNum]=num*val;
  44. Op_Vol[++Op_GoodsNum]=num*vol;
  45. }
  46. }
  47. Multi_Package();
  48. cout<<Package[MAX_Volume]<<endl;
  49. }
  50. return 0;
  51. }

发表评论

表情:
评论列表 (有 0 条评论,420人围观)

还没有评论,来说两句吧...

相关阅读