当前位置:   article > 正文

用动态规划算法解决0-1背包问题_1、试用动态规划算法实现 0-1 背包问题。(30 分)

1、试用动态规划算法实现 0-1 背包问题。(30 分)
用动态规划算法解决0-1背包问题需要了解以下基本概念和原理:
1.使用动态规划算法必须具备两个基本要素:最优子结构性质和重叠子问题性质
2.动态规划算法常以自底向上的方式计算最优值,也就是说,从最小的子问题开始向包含该子问题的大问题方向求解,把每一次求解出的子问题的解保存下来,以便提供给包含该小问题的大问题使用,因此使用循环迭代方式计算更为合理,但从动态规划算法的两个基本要素可以看出,直接以递归方式计算更为简便,于是乎,就产生了动态规划算法的变形方法:备忘录算法。备忘录算法在递归调用过程中将已经求出的子问题的解保存下来,以便下次递归调用遇到相同的子问题可直接获取该子问题的解。
3.动态规划算法在求解最优值的过程中,将遇到的所有子问题的解记录在一张表中,同时也将一些能构造最优解的必要信息记录下来,以便用这些信息构造最优解。求解最优值的过程也俗称“填表过程”。
4.设计动态规划算法的通常的步骤:
(1)找出最优解的性质,并刻画其结构特征
(2)递归地定义最优值
(3)以自底向上的方式计算最优值
(4)根据计算最优值时得到的信息构造最优解
5.0-1背包问题具有最优子结构性质和重叠子问题性质。
定义m(i,j):从物品i、i+1、...、n-1、n中选择物品装入容量大小为j的背包,使该背包的总价
值最大,最大值为m(i,j),也即最优值。
假设背包容量为c,物品总数n,物品i的重量表示wi,价值表示vi,于是0-1背包问题的最优值为
m(1,c)。计算m(1,c)的值之前,先递归地定义最优值的计算方法:
(1)当背包剩余容量为j时,且j >= wi, m(i,j) = max{ m(i+1,j), m(i+1,j-wi)+vi)};说明
此时的背包剩余容量大于物品i的重量,于是在选取物品i和舍弃物品i之中选择较大的。
(2)当背包剩余容量为j时,且0< j < wi, m(i,j) = m(i+1,j); 说明物品i的重量大于背包此时
剩余容量,于是,对物品i舍弃。
(3)最小的子问题,也即只有一个物品时,如何装入背包使背包的总价值最大。假设背包剩余
容量为j时,同时只有一个物品n供选择时,此时,若j > wn, 则最大价值为wn,即 m(n,j) =
wn;若j < wn, 则最大价值为0,即 m(n,j) = 0。

注:上述考虑的物品重量和背包容量都为整数。因此,使用二维数组m[i][j]表示m(i,j)

以下是源代码:

  1. #include "stdafx.h";
  2. #include <iostream>;
  3. using namespace std;
  4. #define C 10
  5. #define N 5
  6. void Knapsack(int *w, int *v, int n, int c, int (*m)[C+1])
  7. {
  8. //先处理记录表格中的最后一行,也即完成对m[n][j]的“填空”
  9. int lowc = w[n]-1 > c ? c : w[n]-1;
  10. for (int j = 0; j <= lowc; j++)
  11. m[n][j] = 0;
  12. for (int j = lowc+1; j <= c; j++)
  13. m[n][j] = v[n];
  14. //循环处理记录表格中的其他行,也即完成对m[i][j]的“填空”
  15. for (int i = n-1; i > 1; i--)
  16. {
  17. lowc = w[i]-1 > c ? c : w[i]-1;
  18. for (int j = 0; j <=lowc; j++)
  19. m[i][j] = m[i+1][j];
  20. for (int j = lowc+1; j <=c; j++)
  21. {
  22. int t1 = m[i+1][j];
  23. int t2 = m[i+1][j - w[i]] + v[i];
  24. m[i][j] = t1 > t2 ? t1 : t2;
  25. }
  26. }
  27. //处理记录表格中的第一行,也即完成对m[1][c]的“填空”
  28. m[1][c] = m[2][c]; //背包容量c不足,物品1舍去
  29. if(c >= w[1] && m[2][c-w[1]] + v[1] > m[2][c]) //背包容量c足够,并且选取物品1的情况价值较大
  30. m[1][c] = m[2][c-w[1]] +v[1];
  31. }
  32. void Traceback(int (*m)[C+1],int *w, int *x,int n, int c)
  33. {
  34. for (int i = 1; i < n; i++)
  35. {
  36. if(m[i][c] == m[i+1][c])
  37. x[i] = 0;
  38. else
  39. {
  40. x[i] = 1;
  41. c -= w[i];
  42. }
  43. }
  44. w[n] < c ? x[n] =1 : x[n] =0;
  45. }
  46. int _tmain()
  47. {
  48. int x[N+1];
  49. int w[N+1] ={-1,2,2,6,5,4};
  50. int v[N+1] ={-1,6,3,5,4,6};
  51. int m[N+1][C+1];
  52. Knapsack(w,v,N,C,m);
  53. cout<<m[1][C]<<endl;
  54. Traceback(m, w, x, N, C);
  55. for (int i = 1; i <= N; i++)
  56. {
  57. cout<<x[i]<<" ";
  58. }
  59. cout<<endl;
  60. system("pause");
  61. return 0;
  62. }

运行结果如下:

15

1 1 0 0 1


从上述算法分析中,有两个较明显的缺点。其一是算法要求所给物品的重量wi是整数。其次,背包容量c很大时,算法所需要的计算时间和空间较多。因此,对上述算法提出改进,使物品重量和背包容量允许为实数(大于0的实数),同时减少所需的时间和空间复杂度。改进分析:
1.将二维表m(i,j)看成函数,对于确定的i,函数m(i,j)是关于变量j的阶梯状单调不减函数。通过跳跃点描述阶梯状的函数m(i,j)。比如,物品n重量大小5,价值为8那么,
当j>=5时,m(n,j) = 8 ;当0<= j < 5时, m(n,j) = 0;可以看出背包容量大小5是m(n,j)的分界容量,所以,(0,0)和(5,8)是函数m(n,j)的两个跳跃点。因此,只需对函数m(i,j)所有的跳跃点保存,无需建立二维表m,从而节省内存空间。最后的最优值从函数m(1,j)的所有跳跃点中获得。
2.每个跳跃点对应一个数组,记录以往选取物品的编号。最优解的构造从函数m(1,j)中的第一个不超过背包容量c对应的跳跃点中获得。(注意,对于确定的i,函数m(i,j)所有的跳跃点按j递增排序。)
3.用p[i+1]表示函数m(i+1,j)的跳跃点集,q[i+1]表示p[i+1]包含物品i之后的跳跃点集,则p[i]等于p[i+1]与q[i+1]的合并(并不是单纯合并,因为有些跳跃点受控于其他的跳跃点)。
举例说明:
背包容量c:5
物品数量n:3
物品重量数组w:2,3,5
物品价值数组v:6,4,8
初始化p[4]的跳跃点集:(0,0)
若加入物品3之后,出现的跳跃点:(5,8),所以合并之后,
p[3]的跳跃点集:(0,0),(5,8)
若加入物品2之后,出现的跳跃点:(3,4),(8,12),所以合并之后,
p[2]的跳跃点集:(0,0),(3,4),(5,8),(8,12)
若加入物品1之后,出现的跳跃点:(2,6),(5,10),(7,14),(10,18),所以合并之后,
p[1]的跳跃点集:(0,0),(2,6),(5,10),(7,14),(10,18)
最终的最优值从p[1]的跳跃点集中寻找,比如:
(1)若背包容量5,对应的跳跃点为(5,10),因此,背包装载物品的最大价值为10.
(2)若背包容量8,对应的跳跃点为(7,14),因此,背包装载物品的最大价值为14.


以下是改进算法:


  1. #include "stdafx.h"
  2. #include <iostream>
  3. using namespace std;
  4. //物品类
  5. class Object
  6. {
  7. friend class Knapsack;
  8. int number;
  9. double weight;
  10. double value;
  11. };
  12. //跳跃点类
  13. class JumpNode
  14. {
  15. friend class Knapsack;
  16. friend void Print();
  17. double enoughw; //排序的依据
  18. double enoughv;
  19. int amount;
  20. int *selected;
  21. JumpNode *next;
  22. };
  23. //0-1背包问题主类
  24. class Knapsack
  25. {
  26. Knapsack(int n, double *w, double *v, double c);
  27. friend void Print();
  28. public:
  29. void DynamicProgram();
  30. private:
  31. int n;
  32. double c;
  33. Object *Ot;
  34. JumpNode *firstp;
  35. JumpNode *firstq;
  36. JumpNode *abandon;
  37. };
  38. //动态规划解决0-1背包问题(物品重量允许大于零的实数)
  39. void Knapsack::DynamicProgram()
  40. {
  41. int i = n;
  42. for (; i >= 1; i--)
  43. {
  44. //对firstp中的结点用物品i进行“升级”,保存在firstq中
  45. JumpNode *jptr = firstp;
  46. JumpNode *lastq = firstq;
  47. bool isfq = true;
  48. while (jptr != nullptr)
  49. {
  50. if(jptr->enoughw + Ot[i].weight <= c)
  51. {
  52. JumpNode *temp;
  53. if(abandon != nullptr)
  54. {
  55. temp = abandon;
  56. abandon = temp->next;
  57. }
  58. else
  59. {
  60. temp = new JumpNode;
  61. temp->selected = new int[n+1];
  62. }
  63. temp->amount = jptr->amount + 1;
  64. temp->enoughw = jptr->enoughw + Ot[i].weight;
  65. temp->enoughv = jptr->enoughv + Ot[i].value;
  66. for (int j = 1; j < temp->amount ; j++)
  67. temp->selected[j] = jptr->selected[j];
  68. temp->selected[temp->amount] = Ot[i].number;
  69. temp->next = nullptr;
  70. //把temp结点插入到firstq中
  71. if(isfq)
  72. {
  73. firstq = temp;
  74. lastq = temp;
  75. isfq = false;
  76. }
  77. else
  78. {
  79. lastq->next = temp;
  80. lastq = temp;
  81. }
  82. }
  83. jptr = jptr->next;
  84. }
  85. if(!isfq)
  86. lastq->next = nullptr;
  87. //对firstp和firstq合并,最后放到firstp
  88. if(firstq != nullptr)
  89. {
  90. JumpNode *firsttemp = nullptr;
  91. JumpNode *temp = nullptr;
  92. bool isfirst = true;
  93. double cmaxvalue = 0;
  94. while ((firstp != nullptr) && (firstq != nullptr))
  95. {
  96. if(firstp->enoughw < firstq->enoughw)
  97. {
  98. if(isfirst)
  99. {
  100. temp = firstp;
  101. firstp = firstp->next;
  102. firsttemp = temp;
  103. isfirst = false;
  104. cmaxvalue = temp->enoughv;
  105. }
  106. else
  107. {
  108. temp->next = firstp;
  109. temp = firstp;
  110. firstp = firstp->next;
  111. cmaxvalue = temp->enoughv;
  112. }
  113. }
  114. else
  115. {
  116. if(firstp->enoughw == firstq->enoughw)
  117. {
  118. if(firstp->enoughv > firstq->enoughv)
  119. {
  120. temp->next = firstp;
  121. temp = firstp;
  122. firstp = firstp->next;
  123. cmaxvalue = temp->enoughv;
  124. JumpNode *ab;
  125. ab = firstq->next;
  126. if(abandon != nullptr) //回收
  127. {
  128. firstq->next = abandon->next;
  129. abandon->next = firstq;
  130. }
  131. else
  132. abandon = firstq;
  133. firstq = ab;
  134. }
  135. else
  136. {
  137. temp->next = firstq;
  138. temp = firstq;
  139. firstq = firstq->next;
  140. cmaxvalue = temp->enoughv;
  141. JumpNode *ab;
  142. ab = firstp->next;
  143. if(abandon != nullptr) //回收
  144. {
  145. firstp->next = abandon->next;
  146. abandon->next = firstp;
  147. }
  148. else
  149. abandon = firstp;
  150. firstp = ab;
  151. }
  152. }
  153. else //firstp->enoughw > firstq->enoughw
  154. {
  155. if(firstq->enoughv > cmaxvalue)
  156. {
  157. temp->next = firstq;
  158. temp = firstq;
  159. firstq = firstq->next;
  160. cmaxvalue = temp->enoughv;
  161. }
  162. else
  163. {
  164. JumpNode *ab;
  165. ab = firstq->next;
  166. if(abandon != nullptr) //回收
  167. {
  168. firstq->next = abandon->next;
  169. abandon->next = firstq;
  170. }
  171. else
  172. abandon = firstq;
  173. firstq = ab;
  174. }
  175. }
  176. }
  177. }//while
  178. if(firstq == nullptr)
  179. while (firstp != nullptr)
  180. {
  181. if(firstp->enoughv <= cmaxvalue) //回收
  182. {
  183. JumpNode *ab;
  184. ab = firstp->next;
  185. if(abandon != nullptr) //回收
  186. {
  187. firstp->next = abandon->next;
  188. abandon->next = firstp;
  189. }
  190. else
  191. abandon = firstp;
  192. firstp = ab;
  193. }
  194. else
  195. {
  196. temp->next = firstp;
  197. temp = firstp;
  198. firstp = firstp->next;
  199. cmaxvalue = temp->enoughv;
  200. }
  201. }
  202. else
  203. {
  204. while (firstq != nullptr)
  205. {
  206. if(firstq->enoughv <= cmaxvalue) //回收
  207. {
  208. JumpNode *ab;
  209. ab = firstq->next;
  210. if(abandon != nullptr) //回收
  211. {
  212. firstq->next = abandon->next;
  213. abandon->next = firstq;
  214. }
  215. else
  216. abandon = firstq;
  217. firstq = ab;
  218. }
  219. else
  220. {
  221. temp->next = firstq;
  222. temp = firstq;
  223. firstq = firstq->next;
  224. cmaxvalue = temp->enoughv;
  225. }
  226. }
  227. }
  228. temp->next = nullptr;
  229. firstp = firsttemp;
  230. }
  231. }//for
  232. }
  233. Knapsack::Knapsack(int n, double *w, double *v, double c):n(n),c(c)
  234. {
  235. Ot = new Object[n+1];
  236. for (int i = 1; i <=n; i++)
  237. {
  238. Ot[i].number = i;
  239. Ot[i].weight = w[i-1];
  240. Ot[i].value = v[i-1];
  241. }
  242. //创建跳跃点(0,0)
  243. JumpNode *newNode = new JumpNode;
  244. newNode->amount = 0;
  245. newNode->enoughv =0;
  246. newNode->enoughw =0;
  247. newNode->next =nullptr;
  248. newNode->selected = new int[n+1];
  249. firstp = newNode;
  250. firstq = nullptr;
  251. }
  252. void Print()
  253. {
  254. const int N = 4; //物品数量
  255. int c = 7; //背包容量
  256. double w[N] = {2,3,5,2}; //物品重量数组
  257. double v[N] = {6.6,4.6,8.5,4.6}; //物品价值数组
  258. Knapsack *K = new Knapsack(N, w, v, c);
  259. K->DynamicProgram();
  260. JumpNode *scan = K->firstp;
  261. int *selarr;
  262. int count = 0;
  263. double selvalue = 0;
  264. JumpNode *oldscan = scan;
  265. while (scan != nullptr)
  266. {
  267. if(scan->enoughw > c)
  268. break;
  269. oldscan = scan;
  270. scan = scan->next;
  271. }
  272. selarr = oldscan->selected;
  273. selvalue = oldscan->enoughv;
  274. count = oldscan->amount;
  275. if(count > 0)
  276. cout<<"已选的物品编号如下:"<<endl; //物品编号从1开始
  277. for (int i = count; i >0; i--)
  278. cout<<selarr[i]<<" ";
  279. if(count == 0)
  280. cout<<"背包容量不足,任何物品都不能装入背包"<<endl;
  281. else
  282. cout<<endl<<"背包装载的物品最大价值:"<<selvalue<<endl;
  283. system("pause");
  284. }
  285. int _tmain()
  286. {
  287. Print();
  288. return 0;
  289. }

运行结果如下:

已选的物品编号如下:

1 2 4

背包装载的物品最大价值:15.8


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

闽ICP备14008679号