1、0029算法笔记回溯法n后问题和01背包问题1、n后问题 问题描述:在nn格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nn格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。 问题解析:用n元数组x1:n表示n后问题的解。其中,xi表示皇后i放在棋盘的第i行的第xi列。由于不允许将2个皇后放在同一列上,所以解向量中的xi互不相同。如果将n*n的棋盘看做是二维方阵,其行号从上到下,列号从左到右依次编号为1,2,n。设两个皇后的坐标分别为(i,j)和(k,l)。若两个皇后在同一斜线上,那么这两个皇
2、后的坐标连成的线为1或者-1。因此有: 由此约束条件剪去不满足行、列和斜线约束的子树。程序的递归回溯实现如下:cppview plaincopy1. /n后问题回溯法计算递归2. #includestdafx.h3. #include4. #includemath.h5. usingnamespacestd;6. 7. classQueen8. 9. friendintnQueen(int);10. private:11. boolPlace(intk);12. voidBacktrack(intt);13. intn,/皇后个数14. *x;/当前解15. longsum;/当前已找到的可行
3、方案数16. ;17. 18. intmain()19. 20. intn=4,m;21. coutn皇后问题的解为:endl;22. m=nQueen(n);23. coutn皇后问题共有;24. coutm个不同的解!endl;25. return0;26. 27. 28. boolQueen:Place(intk)29. 30. for(intj=1;jn)43. 44. sum+;45. for(inti=1;i=n;i+)46. 47. coutxi;48. 49. coutendl;50. 51. else52. 53. /探索第t行的每一列是否有元素满足要求54. for(int
4、i=1;i=n;i+)55. 56. xt=i;57. if(Place(t)58. 59. Backtrack(t+1);60. 61. 62. 63. 64. 65. intnQueen(intn)66. 67. QueenX;68. X.n=n;69. X.sum=0;70. 71. int*p=newintn+1;72. 73. for(inti=0;i=n;i+)74. 75. pi=0;76. 77. 78. X.x=p;79. X.Backtrack(1);80. 81. deletep;82. returnX.sum;83. 数组x记录了解空间树中从根到当前扩展节点的路径,这些
5、信息包含了回溯法在回溯是所需要的信息。利用数组x所含的信息,可将上述回溯法表示成非递归的形式。进一步省去O(n)递归栈空间。迭代实现的n后问题具体代码如下:cppview plaincopy1. /n后问题回溯法计算迭代2. #includestdafx.h3. #include4. #includemath.h5. usingnamespacestd;6. 7. classQueen8. 9. friendintnQueen(int);10. private:11. boolPlace(intk);12. voidBacktrack(void);13. intn,/皇后个数14. *x;/当
6、前解15. longsum;/当前已找到的可行方案数16. ;17. 18. intmain()19. 20. intn=4,m;21. coutn皇后问题的解为:endl;22. m=nQueen(n);23. coutn皇后问题共有;24. coutm个不同的解!endl;25. return0;26. 27. 28. boolQueen:Place(intk)29. 30. for(intj=1;j0)45. 46. xk+=1;47. while(xk=n)&!(Place(k)/寻找能够放置皇后的位置48. 49. xk+=1;50. 51. 52. if(xk=n)/找到位置53.
7、 54. if(k=n)55. 56. for(inti=1;i=n;i+)57. 58. coutxi;59. 60. coutendl;61. sum+;62. 63. else64. 65. k+;66. xk=0;67. 68. 69. else70. 71. k-;72. 73. 74. 75. 76. intnQueen(intn)77. 78. QueenX;79. X.n=n;80. X.sum=0;81. 82. int*p=newintn+1;83. 84. for(inti=0;i0, wi 0, vi 0 , 1in.要求找一n元向量(x1,x2,xn,), xi0,1
8、, wi xic,且 vi xi达最大.即一个特殊的整数规划问题。 问题解析:0-1背包问题是子集选取问题。0-1 背包问题的解空间可以用子集树表示。在搜索解空间树时,只要其左儿子节点是一个可行节点,搜索就进入左子树。当右子树中有可能含有最优解时,才进入右子树搜索。否则,将右子树剪去。设r是当前剩余物品价值总和,cp是当前价值;bestp是当前最优价值。当cp+r=bestp时,可剪去右子树。计算右子树上界的更好的方法是将剩余物品依次按其单位价值排序,然后依次装入物品,直至装不下时,再装入物品一部分而装满背包。 例如:对于0-1背包问题的一个实例,n=4,c=7,p=9,10,7,4,w=3,
9、5,2,1。这4个物品的单位重量价值分别为3,2,3,5,4。以物品单位重量价值的递减序装入物品。先装入物品4,然后装入物品3和1.装入这3个物品后,剩余的背包容量为1,只能装0.2的物品2。由此得一个解为1,0.2,1,1,其相应价值为22。尽管这不是一个可行解,但可以证明其价值是最优值的上界。因此,对于这个实例,最优值不超过22。 在实现时,由Bound计算当前节点处的上界。类Knap的数据成员记录解空间树中的节点信息,以减少参数传递调用所需要的栈空间。在解空间树的当前扩展节点处,仅要进入右子树时才计算上界Bound,以判断是否可将右子树剪去。进入左子树时不需要计算上界,因为上界预期父节点
10、的上界相同。算法的具体实现如下:cppview plaincopy1. /0-1背包问题回溯法求解2. #includestdafx.h3. #include4. usingnamespacestd;5. 6. template7. classKnap8. 9. template10. friendTypepKnapsack(Typep,Typew,Typew,int);11. private:12. TypepBound(inti);13. voidBacktrack(inti);14. 15. Typewc;/背包容量16. intn;/物品数17. 18. Typew*w;/物品重量数组
11、19. Typep*p;/物品价值数组20. Typewcw;/当前重量21. 22. Typepcp;/当前价值23. Typepbestp;/当前最后价值24. ;25. 26. template27. TypepKnapsack(Typepp,Typeww,Typewc,intn);28. 29. template30. inlinevoidSwap(Type&a,Type&b);31. 32. template33. voidBubbleSort(Typea,intn);34. 35. intmain()36. 37. intn=4;/物品数38. intc=7;/背包容量39. in
12、tp=0,9,10,7,4;/物品价值下标从1开始40. intw=0,3,5,2,1;/物品重量下标从1开始41. 42. cout背包容量为:cendl;43. cout物品重量和价值分别为:endl;44. 45. for(inti=1;i=n;i+)46. 47. cout(wi,pi);48. 49. coutendl;50. 51. cout背包能装下的最大价值为:Knapsack(p,w,c,n)endl;52. return0;53. 54. 55. template56. voidKnap:Backtrack(inti)57. 58. if(in)/到达叶子节点59. 60.
13、 bestp=cp;61. return;62. 63. 64. if(cw+wibestp)/进入右子树74. 75. Backtrack(i+1);76. 77. 78. 79. template80. TypepKnap:Bound(inti)/计算上界81. 82. Typewcleft=c-cw;/剩余容量83. Typepb=cp;84. 85. /以物品单位重量价值递减序装入物品86. while(i=n&wi=cleft)87. 88. cleft-=wi;89. b+=pi;90. i+;91. 92. 93. /装满背包94. if(i=n)95. 96. b+=pi/wi
14、*cleft;97. 98. 99. returnb;100. 101. 102. classObject103. 104. template105. friendTypepKnapsack(Typep,Typew,Typew,int);106. public:107. intoperator=a.d);110. 111. private:112. intID;113. floatd;114. ;115. 116. template117. TypepKnapsack(Typepp,Typeww,Typewc,intn)118. 119. /为Knap:Backtrack初始化120. Typ
15、ewW=0;121. TypepP=0;122. 123. Object*Q=newObjectn;124. for(inti=1;i=n;i+)125. 126. Qi-1.ID=i;127. Qi-1.d=1.0*pi/wi;128. P+=pi;129. W+=wi;130. 131. 132. if(W=c)/装入所有物品133. 134. returnP;135. 136. 137. /依物品单位重量价值排序138. BubbleSort(Q,n);139. 140. KnapK;141. K.p=newTypepn+1;142. K.w=newTypewn+1;143. 144.
16、for(inti=1;i=n;i+)145. 146. K.pi=pQi-1.ID;147. K.wi=wQi-1.ID;148. 149. 150. K.cp=0;151. K.cw=0;152. K.c=c;153. K.n=n;154. K.bestp=0;155. 156. /回溯搜索157. K.Backtrack(1);158. 159. deleteQ;160. deleteK.w;161. deleteK.p;162. returnK.bestp;163. 164. 165. template166. voidBubbleSort(Typea,intn)167. 168. /记录一次遍历中是否有元素的交换169. boolexchange;170. for(inti=0;in-1;i+)171. 172. exchange=false;173. for(intj=i+1;j=n-1;j+)174. 175. if(aj=aj-1)176. 177. Swap(aj,aj-1);178. exchange=true;179. 180. 181. /如果这次遍历没有元素的交换,那么排序结束182. if(false=exchange)183. 184. break;185. 186. 187. 188. 189. templatec
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1