当前位置:   article > 正文

以回溯的思想求解0-1背包问题_以0-1背包为例论述回溯的基本思想

以0-1背包为例论述回溯的基本思想

回溯法的思想求解0-1背包问题

目录

介绍

求解


介绍

  • 0-1背包问题

[问题描述]给定n种物品和一背包。物品i的重量是wi,其价值为pi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

  • 回溯法解题的基本思想
  1. 按贪心法的思路,优先装入单位价值高的物品。
  2. 当剩余容量装不下最后考虑的物品时,再用回溯法修改先前的装入方案,直到得到全局最优解为止。

求解

  1. #include <iostream>
  2. #include <iomanip>
  3. #include <algorithm>
  4. #include <cstring>
  5. using namespace std;
  6. struct object
  7. {
  8. int p; //物品的价值
  9. int w; //物品的重量
  10. }*ob;
  11. int cp = 0 , bestp = 0; //cp存放当前的价值,bestp存放最优价值
  12. int num; //物品的数量
  13. int c; //背包的容量
  14. int* x, *bestx;
  15. //自定义大小关系
  16. bool cmp(object a, object b)
  17. {
  18. return a.p/a.w > b.p/b.w;
  19. }
  20. //输出
  21. void putout()
  22. {
  23. cout<<"物品放入背包的状态为:";
  24. for(int i = 0; i < num; i++)
  25. {
  26. cout<<setw(5)<<x[i];
  27. }
  28. cout<<" 获得的价值为:"<<cp<<endl;
  29. }
  30. //回溯法求0-1背包
  31. void knap(int t) //t表示递归深度
  32. {
  33. if(t >= num) //到达叶子节点
  34. {
  35. if(bestp < cp)
  36. {
  37. bestp = cp;
  38. for(int i = 0; i < num; i++)
  39. {
  40. bestx[i] = x[i];
  41. }
  42. }
  43. putout();
  44. }
  45. else
  46. {
  47. if(ob[t].w <= c) //搜索左枝,约束条件
  48. {
  49. x[t] = 1; cp += ob[t].p; c -= ob[t].w; //改变状态
  50. knap(t+1); //继续向下深度搜索
  51. x[t] = 0; cp -= ob[t].p; c += ob[t].w; //恢复原有状态
  52. }
  53. x[t] = 0; //搜索右枝,无需约束条件
  54. knap(t+1);
  55. }
  56. }
  57. int main()
  58. {
  59. cout<<"请输入物品的数量:";
  60. cin>>num;
  61. ob = new object[num];
  62. cout<<"请输入每个物品的重量:";
  63. for(int i = 0; i < num; i++)
  64. {
  65. cin>>ob[i].w;
  66. }
  67. cout<<"请输入每个物品的价值:";
  68. for(int i = 0; i < num; i++)
  69. {
  70. cin>>ob[i].p;
  71. }
  72. cout<<"请输入背包的容量:";
  73. cin>>c;
  74. x = new int[num]; //存放当前解
  75. bestx = new int[num]; //存放最优解
  76. sort(ob,ob+num,cmp); //将物品根据单位价值从高到低排列
  77. knap(0);
  78. cout<<"最优价值:"<<bestp<<"\t";
  79. for(int i = 0; i < num; i++)
  80. {
  81. if(bestx[i] == 1)
  82. {
  83. cout<<i+1<<" ";
  84. }
  85. }
  86. return 0;
  87. }

据此形成的部分空间树,如下图所示,其中的w[i]与代码中的ob[i].w相对应

运行结果截图:


前面算法完全搜索解空间树策略: 用约束条件确定是否搜索其左子树; 对右子树没有任何判断,一定进入右子树搜索,十分耗时,下面在原有代码基础上加入剪枝函数(右子树有可能包含最优解时才进入右子树搜索,否则将右子树减去)以优化。

剪枝方法具体如下:

  1. r:当前剩余物品价值总和;
  2. cv:当前获得价值;
  3. bestp:当前最优价值;
  4. 当cv+r<=bestp时,可剪去右子树。

计算右子树上界方法:

将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界。

代码如下:

  1. #include <iostream>
  2. #include <iomanip>
  3. #include <algorithm>
  4. #include <cstring>
  5. using namespace std;
  6. struct object
  7. {
  8. int p; //物品的价值
  9. int w; //物品的重量
  10. }*ob;
  11. int cp = 0 , bestp = 0; //cp存放当前的价值,bestp存放最优价值
  12. int num; //物品的数量
  13. int c; //背包的容量
  14. int cw = 0; //计算当前的重量
  15. int* x, *bestx;
  16. //自定义大小关系
  17. bool cmp(object a, object b)
  18. {
  19. return a.p/a.w > b.p/b.w;
  20. }
  21. //输出
  22. void putout()
  23. {
  24. cout<<"物品放入背包的状态为:";
  25. for(int i = 0; i < num; i++)
  26. {
  27. cout<<setw(5)<<x[i];
  28. }
  29. cout<<" 获得的价值为:"<<cp<<endl;
  30. }
  31. //计算以结点i处为根节点的上界(限界条件)
  32. int bound(int i)
  33. {
  34. int left = c - cw; //剩余容量
  35. int temp = cp; //当前价值
  36. while(ob[i].w <= left && i <= num)
  37. {
  38. temp += ob[i].p;
  39. left -= ob[i].w;
  40. i++;
  41. }
  42. //装满背包
  43. if(i <= num)
  44. {
  45. temp += left*ob[i].p/ob[i].w;
  46. }
  47. return temp;
  48. }
  49. //回溯法求0-1背包
  50. void knap(int t) //t表示递归深度
  51. {
  52. if(t >= num) //到达叶子节点
  53. {
  54. if(bestp < cp)
  55. {
  56. bestp = cp;
  57. for(int i = 0; i < num; i++)
  58. {
  59. bestx[i] = x[i];
  60. }
  61. }
  62. putout();
  63. }
  64. else
  65. {
  66. if(ob[t].w <= c) //搜索左枝,约束条件
  67. {
  68. x[t] = 1; cp += ob[t].p; c -= ob[t].w; //改变状态
  69. knap(t+1); //继续向下深度搜索
  70. x[t] = 0; cp -= ob[t].p; c += ob[t].w; //恢复原有状态
  71. }
  72. if(bound(t+1) > bestp) //限界搜索右枝
  73. {
  74. x[t] = 0;
  75. knap(t+1);
  76. }
  77. }
  78. }
  79. int main()
  80. {
  81. cout<<"请输入物品的数量:";
  82. cin>>num;
  83. ob = new object[num];
  84. cout<<"请输入每个物品的重量:";
  85. for(int i = 0; i < num; i++)
  86. {
  87. cin>>ob[i].w;
  88. }
  89. cout<<"请输入每个物品的价值:";
  90. for(int i = 0; i < num; i++)
  91. {
  92. cin>>ob[i].p;
  93. }
  94. cout<<"请输入背包的容量:";
  95. cin>>c;
  96. x = new int[num]; //存放当前解
  97. bestx = new int[num]; //存放最优解
  98. sort(ob,ob+num,cmp); //将物品根据单位价值从高到低排列
  99. knap(0);
  100. cout<<"最优价值:"<<bestp<<"\t";
  101. for(int i = 0; i < num; i++)
  102. {
  103. if(bestx[i] == 1)
  104. {
  105. cout<<i+1<<" ";
  106. }
  107. }
  108. return 0;
  109. }

由此形成的空间树为

运行结果:

至此问题得到了一种解答。

注:这是本人第一次写博客,如有不足之处敬请批评指教!

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

闽ICP备14008679号