当前位置:   article > 正文

用回溯法解决0-1背包问题_用回溯法解决0-1背包问题不调用bound()函数

用回溯法解决0-1背包问题不调用bound()函数
用回溯法解决0-1背包问题需要解决一下问题:
1.如何动态生成子集树
2.如何设计子集树中的结点类型
3.如何设计两个剪枝函数:约束函数和限界函数
4.如何保存一个或多个最优解,同时保存最优值

解决方法:
1.子集树通过动态的方式生成,子集树中的结点类型共用物品类型,其中结点之间的父子关系通过递归调用的方式关联,这种关系并不在类中设置变量显示表示。
2.为了方便限界函数的计算和程序中的使用,先对物品预处理,以物品的单位价值重量进行降序排序。

3.设置程序运行时一个构造最优解变量,该变量对应的最优值与当前最优解对于的最优值对比,如果优于当前最优解,则将覆盖当前最优解;如果与当前最优解对应的值相等,则同时保存两个最优解。


以下是具体的源代码:

  1. #include "stdafx.h"
  2. #include <iostream>
  3. using namespace std;
  4. typedef int Typew;
  5. typedef int Typep;
  6. //物品类
  7. class Object{
  8. friend class Knap;
  9. public:
  10. int operator <= (Object a) const{
  11. return (d >= a.d);
  12. }
  13. private:
  14. int ID; //物品编号
  15. Typew w; //物品重量
  16. Typep p; //物品价值
  17. float d; //单位重量价值
  18. };
  19. //0-1背包问题的主类
  20. class Knap{
  21. public:
  22. Knap(Typew *w, Typep *p, Typew c, int n);
  23.  Typep Knapsack();//回溯法解决0-1背包问题的主函数
  24. //回溯法求解01背包问题
  25. void BackTrack(int floor);
  26. //负责打印最优值和最优解,以物品编号的顺序打印结果
  27. void print();
  28. private:
  29. //计算结点价值上界
  30. Typep Bound(int i);
  31. Typew c; //背包容量
  32. int n; //物品总数
  33. Object *Q; //在Q数组中存放的物品以单位重量价值降序排序
  34. Typew cw; //当前装包重量
  35. Typep cp; //当前装包价值
  36. int *cbestx; //当前最优解
  37. int count; //最优解的个数
  38. int *bestx[10]; //最优解,最优解的个数不超过10个。
  39. Typep bestp; //最优值
  40. Typep oldbestp; //用于回溯法边界处理,保存上一次最优值
  41. };
  42. Knap::Knap(Typew *w, Typep *p, Typew c, int n)
  43. {
  44. //初始化
  45. Typew W = 0;
  46. Typep P = 0;
  47. count = 0;
  48. this->c = c;
  49. oldbestp = 0;
  50. this->n = n;
  51. cw = 0;
  52. cp = 0;
  53. Q = new Object[n];
  54. for(int i =0; i<n; i++)
  55. {
  56. Q[i].ID = i+1;
  57. Q[i].d = 1.0*p[i]/w[i];
  58. Q[i].w = w[i];
  59. Q[i].p = p[i];
  60. P += p[i];
  61. W += w[i];
  62. }
  63. //所有物品的总重量小于等于背包容量c
  64. if (W <= c)
  65. {
  66. bestp = P;
  67. int *newbestx = new int[n];
  68. for(int i =0; i<n; i++)
  69. {
  70. newbestx[i] = 1;
  71. }
  72. bestx[count++] = newbestx;
  73. }
  74. //所有物品的总重量大于背包容量c,存在最佳装包方案
  75. //采用简单冒泡排序
  76. for(int i = 0; i<n-1; i++)
  77. for(int j = 0; j<n-i-1; j++)
  78. {
  79. if(Q[j].d < Q[j+1].d)
  80. {
  81. Object temp = Q[j];
  82. Q[j] = Q[j+1];
  83. Q[j+1] = temp;
  84. }
  85. }
  86. }
  87. Typep Knap::Knapsack()
  88. {
  89. if(count > 0) //背包容量足够大,在初始化时已经将所有物品装入背包
  90. {
  91. print();
  92. return bestp;
  93. }
  94. else //背包容量小于物品所有重量,存在最优装包方案
  95. {
  96. cbestx = new int[n];
  97. BackTrack(0); //从数组Q下标0,首结点开始回溯法求解
  98. }
  99. }
  100. void Knap::BackTrack(int floor)
  101. {
  102. if(floor > n-1) //已经到了子集树中的叶子结点
  103. {
  104. if( cp == oldbestp ) //说明可能有多个最优解
  105. {
  106. int *newbe = new int[n];
  107. for (int i = 0; i < n; i++)
  108. {
  109. newbe[i] = cbestx[i];
  110. }
  111. bestx[count++] = newbe;
  112. }
  113. if( cp > oldbestp) //说明最优解需要更新同时只有一个
  114. {
  115. count = 0;
  116. int *newbe = new int[n];
  117. for (int i = 0; i < n; i++)
  118. {
  119. newbe[i] = cbestx[i];
  120. }
  121. bestx[count++] = newbe;
  122. oldbestp = cp;
  123. }
  124. }
  125. else
  126. {
  127. //选取数组Q下标为floor的物品,满足背包容量约束
  128. if (c >= cw + Q[floor].w)
  129. {
  130. cw += Q[floor].w;
  131. cp += Q[floor].p;
  132. if(cp >= bestp)
  133. bestp = cp;
  134. cbestx[floor] = 1;
  135. BackTrack(floor + 1);
  136. cw -= Q[floor].w;
  137. cp -= Q[floor].p;
  138. }
  139. //舍去数组Q下标为floor的物品,满足限界函数
  140. if(cp + Bound(floor + 1) >= bestp)
  141. {
  142. cbestx[floor] = 0;
  143. BackTrack(floor + 1);
  144. }
  145. }
  146. }
  147. void Knap::print()
  148. {
  149. Typep *original = new int[n+1];
  150. cout<<"以下每行为一种解法:"<<endl;
  151. for (int i = count-1; i >= 0; i--)
  152. {
  153. for (int j = 0; j < n; j++)
  154. {
  155. original[Q[j].ID] = bestx[i][j];
  156. }
  157. for (int k = 1; k <= n; k++)
  158. {
  159. cout<< original[k] <<" ";
  160. }
  161. cout<<endl;
  162. }
  163. cout<<"最优解的个数:"<<count<<endl;
  164. cout<<"最优值:"<<bestp<<endl;
  165. }
  166. Typep Knap::Bound(int i)
  167. {
  168. Typew cleft = c - cw;
  169. Typep b = cp;
  170. while (i < n && Q[i].w <= cleft)
  171. {
  172. cleft -= Q[i].w;
  173. b += Q[i].p;
  174. i++;
  175. }
  176. if(i < n) b += Q[i].p/Q[i].w * cleft;
  177. return b;
  178. }
  179. int _tmain(int argc, _TCHAR* argv[])
  180. {
  181. const int N = 4;
  182. Typew c = 7;
  183. Typew w[N] = {2,3,5,2};
  184. Typep p[N] = {6,4,8,4};
  185. cout<<"背包容量:"<<c <<" ,物品总数:"<< N<<endl;
  186. cout<<"物品重量数组:";
  187. for (int i = 0; i < N; i++)
  188. {
  189. cout<<w[i]<<" ";
  190. }
  191. cout<<endl;
  192. cout<<"物品价值数组:";
  193. for (int i = 0; i < N; i++)
  194. {
  195. cout<<p[i]<<" ";
  196. }
  197. cout<<endl;
  198. Knap K(w, p, c, N);
  199. K.Knapsack();
  200. K.print();
  201. system("pause");
  202. return 0;
  203. }
运行结果如下图:


本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号