实验04 回溯法班级:0920561 姓名:宋建俭学号:20一、实验目的1.掌握回溯法的基本思想。
2.掌握回溯法中问题的解空间、解向量、显式约束条件、隐式约束条件以及子集树与排列树的递归算法结构等内容。
3.掌握回溯法求解具体问题的方法。
二、实验要求1.认真阅读算法设计教材,了解回溯法思想及方法;2.设计用回溯算法求解装载问题、n后问题、图的m着色问题的java程序三、实验内容1.有一批共n个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为wi,且∑wi≤C1+C2。
装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。
如果有,找出一种装载方案。
2.在n×n格的棋盘上放置彼此不受攻击的n个皇后。
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。
3.给定无向连通图G和m种不同的颜色。
用这些颜色为图G的各顶点着色,每个顶点着一种颜色。
是否有一种着色法使G中每条边的2个顶点着不同颜色。
这个问题是图的m可着色判定问题。
四、算法原理1、装载问题用回溯法解装载问题时,用子集树表示其解空间是最合适的。
可行性约束可剪去不满足约束条件(w1x1+w2x2+…+wnxn)<=c1的子树。
在子集树的第j+1层结点Z处,用cw记当前的装载重量,即cw=(w1x1+w2x2+…+wjxj),当cw>c1时,以结点Z为根的子树中所有结点都不满足约束条件,因而该子树中的解均为不可行解,故可将该子树剪去。
解装载问题的回溯法中,方法maxLoading返回不超过c的最大子集和,但未给出达到这个最大子集和的相应子集。
算法maxLoading调用递归方法backtrack(1)实现回溯搜索。
Backtrack(i)搜索子集树中第i层子树。
类Loading的数据成员记录子集树结点信息,以减少传给backtrack的参数。
cw记录当前结点相应的装载重量,bestw记录当前最大装载重量。
在算法backtrack中,当i>n时,算法搜索至叶结点,其相应的装载重量cw。
如果cw>bestw,则表示当前解优于当前最优解,此时应更新bestw。
当i<=n时,当前扩展结点Z是子集树的内部结点。
该结点有x[i]=1和x[i]=0两个儿子结点。
其左儿子结点表示x[i]=1的情形,仅当cw+w[i]<=c时进入左子树,对左子树递归搜索。
其右儿子结点表示x[i]=0的情形。
由于可行结点的右儿子结点总是可行的,故进入右子树时不需检查可行性。
算法backtrack动态地生成问题的解空间树。
在每个结点处算法花费O(1)时间。
子集树中结点个数为O(2的n次方),故backtrack所需的计算时间为O(2的n 次方)。
另外backtrack还需要额外的O(n)递归栈空间。
2、n后问题用n元祖x[1:n]表示n后问题的解。
其中x[i]表示皇后i放在棋盘的第i行的第x[i]列。
由于不允许将2个皇后放在同一列,所以解向量中的x[i]互不相同。
2个皇后不能放在同一斜线上是问题的隐约束。
对于一般的n后问题,这一隐约束条件可以化成显约束的形式。
将n×n格棋盘看作二维方阵,其行号从上到下,列号从左到右依次编号为1,2,…,n。
从棋盘左上角到右下角的主对角线及其平行线(即斜率为-1的各斜线)上,2个下标值的差(行号-列号)值相等。
同理,斜率为+1的每一条斜线上,2个下标值的和(行号+列号)值相等。
因此,若2个皇后放置的位置分别是(i,j)和(k,l),且i-j=k-l或i+j=k+l,则说明这2个皇后处于同一斜线上。
以上2个方程分别等价于i-k=j-l和i-k=l-j。
由此可知,只要|i-k|=|j-l|成立,就表明2个皇后位于同一条斜线上。
问题的隐约束化成了显约束。
用回溯法解n后问题时,用完全n叉树表示解空间。
可行性约束place剪去不满足行、列和斜线约束的子树。
解n后问题的回溯法中,递归方法backtrack(1)实现对整个解空间的回溯搜索。
Backtrack(i)搜索解空间中第i层子树。
类Queen的数据成员记录解空间中节点信息,以减少传给backtrack的参数。
Sum记录当前已找到的可行方案数。
在算法backtrack中,当i>n时,算法搜索至叶节点,得到一个新的n皇后互不攻击方案,当前已找到的可行方案数sum曾1.当i<=n时,当前扩展节点Z是解空间中的内部结点。
该结点有x[i]=1,2,…,n,共n个儿子结点。
对当前扩展结点Z的每一个儿子结点,由place检查其可行性,并以深度优先的方式递归地对可行子树搜索,或剪去不可行子树。
3、图的m着色问题给定图G=(V,E)和m种颜色,用图的邻接矩阵a表示无向连接图G=(V,E)。
若(i,j)属于图G=(V,E)的边集E,则a[i][j]=0。
整数1,2,…,m用来表示m种不同颜色。
顶点i所着颜色用x[i]表示。
数组x[1:n]是问题的解向量。
问题的解空间可表示为一棵高度为n+1的完全m叉树。
解空间树的第i(1<=i<=n)层中每一结点都有m个儿子,每个儿子相应于x[i]的m个可能的着色之一。
第n+1层结点均为叶节点。
在解图的m着色问题的回溯法中,backtrack(i)搜索解空间中第i层子树。
类Coloring的数据成员记录解空间中结点信息,以减少传给backtrack的参数。
Sum记录当前找到的m可着色方案数。
在算法backtrack中,当i>n时,算法搜索至叶结点,得到新的m着色方案,当前找到的m可着色方案数sum增1。
当i<=n时,当前扩展结点Z是解空间中的内部结点。
该结点有x[i]=1,2,…,m 共m个儿子结点。
对当前扩展结点Z的每一个儿子结点,由方案ok检查其可行性,并以深度优先的方式递归地对可行子树搜索,或剪去不可行子树。
五、实验程序功能模块1、装载问题import ;public class MainLoading {public static void main(String[] args) throws IOException { Loading loading = new Loading();int n = 4;"集装箱个数: " + n);int[] x = new int[n + 1];String[] b = new String[n];String a = "30 50 20 70";"集装箱重量: " + a);int[] w = new int[n + 1];w[1] = 30;w[2] = 50;w[3] = 20;w[4] = 70;int c1 = 130;int c2 = 70;"轮船载重量: " + c1 + "," + c2);int result = loading.maxloading(w, c1, x);int max, temp;for (int i = 1; i < 3; i++) {for (int j = 2; j < 3; j++) {if (w[i] > w[j]) {temp = w[i];w[i] = w[j];w[j] = temp;}}}if ((w[3] > c1) && (w[3] > c2)) {"都不可装");}else {"轮船1装载的集装箱:");for (int u = 1; u < n + 1; u++)if (loading.bestx[u] == 1)+ " ");if (loading.r > (result + c2))"轮船1可装:" + result + " " + "轮船2装不完.");else {"轮船2装载的集装箱:");for (int u = 1; u < n + 1; u++)if (loading.bestx[u] == 0)+ " ");"最优装载--轮船1:" + result + " " + "轮船2:"+ (loading.r - result));}}}}public class Loading {static int n;static int[] w;static int c1, c2; // 第一、二艘轮船的载重量static int cw;static int bestw;static int r;static int[] x;static int[] bestx;public static int maxloading(int[] ww, int cc, int[] xx) { n = ww.length - 1;w = ww;c1 = cc;bestw = 0;cw = 0;x = new int[n + 1];bestx = xx;r = 0;for (int i = 1; i <= n; i++)r += w[i];backtrack(1);return bestw;}private static void backtrack(int i) {if (i > n) {if (cw > bestw) {for (int j = 1; j <= n; j++)bestx[j] = x[j];bestw = cw;}return;}r -= w[i];if (cw + w[i]<=c1){x[i] = 1;cw += w[i];backtrack(i + 1);cw -= w[i];}if (cw + r > bestw) {x[i] = 0;backtrack(i + 1);}r += w[i];}}2、n后问题import ;public class MainNQueen {public static void main(String[] args) {nQueen nqueen = new nQueen();int nn; // 输入n值int sum; // 可行方案个数try {nn = 4; // 将输入值转换为int保存if (nn <= 0) {throw new IOException("不能输入负数");}"方格数是:" + nn);sum = nqueen.nQueen(nn); // 调用nqueen方法"可行方案个数为:" + sum); // 输出sum} catch (IOException e) {;} catch (NumberFormatException e) {"请输入数字。