0029算法笔记回溯法n后问题和01背包问题.docx
- 文档编号:3538595
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:15
- 大小:73.99KB
0029算法笔记回溯法n后问题和01背包问题.docx
《0029算法笔记回溯法n后问题和01背包问题.docx》由会员分享,可在线阅读,更多相关《0029算法笔记回溯法n后问题和01背包问题.docx(15页珍藏版)》请在冰豆网上搜索。
0029算法笔记回溯法n后问题和01背包问题
1、n后问题
问题描述:
在n×n格的棋盘上放置彼此不受攻击的n个皇后。
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。
问题解析:
用n元数组x[1:
n]表示n后问题的解。
其中,x[i]表示皇后i放在棋盘的第i行的第x[i]列。
由于不允许将2个皇后放在同一列上,所以解向量中的x[i]互不相同。
如果将n*n的棋盘看做是二维方阵,其行号从上到下,列号从左到右依次编号为1,2,……n。
设两个皇后的坐标分别为(i,j)和(k,l)。
若两个皇后在同一斜线上,那么这两个皇后的坐标连成的线为1或者-1。
因此有:
由此约束条件剪去不满足行、列和斜线约束的子树。
程序的递归回溯实现如下:
[cpp] viewplain copy
1.//n后问题 回溯法计算 递归
2.#include "stdafx.h"
3.#include
4.#include "math.h"
5.using namespace std;
6.
7.class Queen
8.{
9. friend int nQueen(int);
10. private:
11. bool Place(int k);
12. void Backtrack(int t);
13. int n, // 皇后个数
14. *x; // 当前解
15. long sum; // 当前已找到的可行方案数
16.};
17.
18.int main()
19.{
20. int n=4,m;
21. cout< "< 22. m=nQueen(n); 23. cout< 24. cout< "< 25. return 0; 26.} 27. 28.bool Queen: : Place(int k) 29.{ 30. for (int j=1;j 31. { 32. if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) 33. { 34. return false; 35. } 36. } 37. return true; 38.} 39. 40.void Queen: : Backtrack(int t)//t扩展的是行 41.{ 42. if (t>n) 43. { 44. sum++; 45. for (int i=1;i<=n;i++) 46. { 47. cout< 48. } 49. cout< 50. } 51. else 52. { 53. //探索第t行的每一列是否有元素满足要求 54. for (int i=1;i<=n;i++) 55. { 56. x[t]=i; 57. if (Place(t)) 58. { 59. Backtrack(t+1); 60. } 61. } 62. } 63. } 64. 65.int nQueen(int n) 66.{ 67. Queen X; 68. X.n=n; 69. X.sum=0; 70. 71. int *p=new int[n+1]; 72. 73. for(int i=0;i<=n;i++) 74. { 75. p[i]=0; 76. } 77. 78. X.x=p; 79. X.Backtrack (1); 80. 81. delete []p; 82. return X.sum; 83.} 数组x记录了解空间树中从根到当前扩展节点的路径,这些信息包含了回溯法在回溯是所需要的信息。 利用数组x所含的信息,可将上述回溯法表示成非递归的形式。 进一步省去O(n)递归栈空间。 迭代实现的n后问题具体代码如下: [cpp] viewplain copy 1.//n后问题 回溯法计算 迭代 2.#include "stdafx.h" 3.#include 4.#include "math.h" 5.using namespace std; 6. 7.class Queen 8.{ 9. friend int nQueen(int); 10. private: 11. bool Place(int k); 12. void Backtrack(void); 13. int n, // 皇后个数 14. *x; // 当前解 15. long sum; // 当前已找到的可行方案数 16.}; 17. 18.int main() 19.{ 20. int n=4,m; 21. cout< "< 22. m=nQueen(n); 23. cout< 24. cout< "< 25. return 0; 26.} 27. 28.bool Queen: : Place(int k) 29.{ 30. for (int j=1;j 31. { 32. if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) 33. { 34. return false; 35. } 36. } 37. return true; 38.} 39. 40.void Queen: : Backtrack() 41.{ 42. x[1] = 0; 43. int k = 1; 44. while(k>0) 45. { 46. x[k] += 1; 47. while((x[k]<=n)&&! (Place(k)))//寻找能够放置皇后的位置 48. { 49. x[k] += 1; 50. } 51. 52. if(x[k]<=n)//找到位置 53. { 54. if(k == n) 55. { 56. for (int i=1;i<=n;i++) 57. { 58. cout< 59. } 60. cout< 61. sum++; 62. } 63. else 64. { 65. k++; 66. x[k]=0; 67. } 68. } 69. else 70. { 71. k--; 72. } 73. } 74. } 75. 76.int nQueen(int n) 77.{ 78. Queen X; 79. X.n=n; 80. X.sum=0; 81. 82. int *p=new int[n+1]; 83. 84. for(int i=0;i<=n;i++) 85. { 86. p[i]=0; 87. } 88. 89. X.x=p; 90. X.Backtrack(); 91. 92. delete []p; 93. return X.sum; 94.} 程序运行结果如图: 2、0-1背包问题 问题描述: 给定n种物品和一背包。 物品i的重量是wi,其价值为vi,背包的容量为C。 问: 应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 形式化描述: 给定c>0,wi>0,vi>0,1≤i≤n.要求找一n元向量(x1,x2,…,xn,),xi∈{0,1},∋∑wixi≤c,且∑vixi达最大.即一个特殊的整数规划问题。 问题解析: 0-1背包问题是子集选取问题。 0-1背包问题的解空间可以用子集树表示。 在搜索解空间树时,只要其左儿子节点是一个可行节点,搜索就进入左子树。 当右子树中有可能含有最优解时,才进入右子树搜索。 否则,将右子树剪去。 设r是当前剩余物品价值总和,cp是当前价值;bestp是当前最优价值。 当cp+r<=bestp时,可剪去右子树。 计算右子树上界的更好的方法是将剩余物品依次按其单位价值排序,然后依次装入物品,直至装不下时,再装入物品一部分而装满背包。 例如: 对于0-1背包问题的一个实例,n=4,c=7,p=[9,10,7,4],w=[3,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,以判断是否可将右子树剪去。 进入左子树时不需要计算上界,因为上界预期父节点的上界相同。 算法的具体实现如下: [cpp] viewplain copy 1.//0-1背包问题 回溯法求解 2.#include "stdafx.h" 3.#include 4.using namespace std; 5. 6.template 7.class Knap 8.{ 9. template 10. friend Typep Knapsack(Typep [],Typew [],Typew,int); 11. private: 12. Typep Bound(int i); 13. void Backtrack(int i); 14. 15. Typew c; //背包容量 16. int n; //物品数 17. 18. Typew *w; //物品重量数组 19. Typep *p; //物品价值数组 20. Typew cw; //当前重量 21. 22. Typep cp; //当前价值 23. Typep bestp;//当前最后价值 24.}; 25. 26.template 27.Typep Knapsack(Typep p[],Typew w[],Typew c,int n); 28. 29.template 30.inline void Swap(Type &a,Type &b); 31. 32.template 33.void BubbleSort(Type a[],int n); 34. 35.int main() 36.{ 37. int n = 4;//物品数 38. int c = 7;//背包容量 39. int p[] = {0,9,10,7,4};//物品价值 下标从1开始 40. int w[] = {0,3,5,2,1};//物品重量 下标从1开始 41. 42. cout<<"背包容量为: "< 43. cout<<"物品重量和价值分别为: "< 44. 45. for(int i=1; i<=n; i++) 46. { 47. cout<<"("< 48. } 49. cout< 50. 51. cout<<"背包能装下的最大价值为: "< 52. return 0; 53.} 54. 55.template 56.void Knap : Backtrack(int i) 57.{ 58. if(i>n)//到达叶子节点 59. { 60. bestp = cp; 61. return; 62. } 63. 64. if(cw + w[i] <= c)//进入左子树 65. { 66. cw += w[i]; 67. cp += p[i]; 68. Backtrack(i+1); 69. cw -= w[i]; 70. cp -= p[i]; 71. } 72. 73. if(Bound(i+1)>bestp)//进入右子树 74. { 75. Backtrack(i+1); 76. } 77.} 78. 79.template 80.Typep Knap : Bound(int i)// 计算上界 81.{ 82. Typew cleft = c - cw; // 剩余容量 83. Typep b = cp; 84. 85. // 以物品单位重量价值递减序装入物品 86. while (i <= n && w[i] <= cleft) 87. { 88. cleft -= w[i]; 89. b += p[i]; 90. i++; 91. } 92. 93. // 装满背包 94. if (i <= n) 95. { 96. b += p[i]/w[i] * cleft; 97. } 98. 99. return b; 100.} 101. 102.class Object 103.{ 104. template 105. friend Typep Knapsack(Typep[],Typew [],Typew,int); 106. public: 107. int operator <= (Object a)const 108. { 109. return (d>=a.d); 110. } 111. private: 112. int ID; 113. float d; 114.}; 115. 116.template 117.Typep Knapsack(Typep p[],Typew w[],Typew c,int n) 118.{ 119. //为Knap: : Backtrack初始化 120. Typew W = 0; 121. Typep P = 0; 122. 123. Object *Q = new Object[n]; 124. for(int i=1; i<=n; i++) 125. { 126. Q[i-1].ID = i; 127. Q[i-1].d = 1.0 * p[i]/w[i]; 128. P += p[i]; 129. W += w[i]; 130. } 131. 132. if(W <= c)//装入所有物品 133. { 134. return P; 135. } 136. 137. //依物品单位重量价值排序 138. BubbleSort(Q,n); 139. 140. Knap 141. K.p = new Typep[n+1]; 142. K.w = new Typew[n+1]; 143. 144. for(int i=1; i<=n; i++) 145. { 146. K.p[i] = p[Q[i-1].ID]; 147. K.w[i] = w[Q[i-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. delete []Q; 160. delete []K.w; 161. delete []K.p; 162. return K.bestp; 163.} 164. 165.template 166.void BubbleSort(Type a[],int n) 167.{ 168. //记录一次遍历中是否有元素的交换 169. bool exchange; 170. for(int i=0; i 171. { 172. exchange = false ; 173. for(int j=i+1; j<=n-1; j++) 174. { 175. if(a[j]<=a[j-1]) 176. { 177. Swap(a[j],a[j-1]); 178. exchange = true; 179. } 180. } 181. //如果这次遍历没有元素的交换,那么排序结束 182. if(false == exchange) 183. { 184. break ; 185. } 186. } 187.} 188. 189.template
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 0029 算法 笔记 回溯 问题 01 背包