浏览 6408 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-21
回溯法是在包含问题的所有解得解空间树(或森林)中,按照深度优先的策略,从根结点出发搜索解空间树。 算法搜索至解空间树的任一结点时,总是先判断该结点是否满足问题的约束条件。如果满足进入该子树,继续 按照深度优先的策略进行搜索。否则,不去搜索以该结点为根的子树,而是逐层向其祖先结点回溯。 回溯法在用来求解问题的所有解时,要回溯到根,且根结点的所有可行的子树都已被搜索遍才结束。而回溯法 在用来求解问题的任一解时,只要搜索到问题的一个解就可以结束。适用于解决一些最优化问题。 2. 算法设计过程 (1) 确定问题的解空间 应用回溯法解决问题时,首先应明确定义问题的解空间。问题的解空间应至少包含问题的一个最优解。 (2) 确定结点的扩展规则 约束条件。 (3) 搜索解空间 回溯算法从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也 成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并 成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应该往回 移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中 搜索,直至找到所要求的解或解空间中已没有活结点时为止。 3. 算法框架 (1) 问题框架 设问题的解是一个n维向量(a1,a2,...,an),约束条件是ai(i1,2,...,n)之间满足某种条件,记为f(ai)。 (2) 非递归回溯框架 int a[n], i; i=1; while(i>0(有路可走) and [未达到目标]){ //还未回溯到头 if(i>n){ //搜索到叶结点 搜索到一个解,输出; }else{ a[i]第一个可能的值; while(a[i]不满足约束条件且在搜索空间内) a[i]下一可能的值; if(a[i]在搜索空间内){ 标识占用的资源; i = i+1; //扩展下一个结点 }else{ 清理所占的状态空间; i = i-1; //回溯 } } } (3)递归算法框架 int a[n]; try(int i){ if(i>n){ 输出结果; }else{ for(j=下界; j<=上界; j++){//枚举i所有可能的路径 if(f(j)){ //满足限界函数和约束条件 a[i] = j; ... //其他操作 try(i+1); a[i] = 0; //回溯前的清理工作(如a[i]置空) } } } } 4. 一个例子 (1) 问题描述 马的遍历问题。 在n*m的棋盘中,马只能走"日"字。马从位置(x,y)出发,把棋盘的每一格都走一次且只走一次。找出所有路径。 (2) 问题分析 马是在棋盘的点上行走的,所以这里的棋盘是指行有N条边、列有M条边。而一个马在不出边界的情况下有8个方向 可以行走(走"日"字),如当前坐标为(x,y),则行走后的坐标可以为: (x+1, y+2) (x+1, y-2) (x+2, y+1) (x+2, y-1) (x-1, y-2) (x-1, y+2) (x-2, y-1) (x-2, y+1) 搜索的解空间是整个棋盘上的n*m个点。 约束条件是不出边界且每个点只经过一次。 搜索过程是从任一点(x,y)出发,按照深度优先的原则,从8个方向中尝试一个可以走的点,直到走过棋盘上所有 n*m个点。 5. Java代码实现 package test; /** * create on 2010.05.21 TODO 回溯算法 * * @author 毛正吉 * @version v1.0 * */ public class RecollectionSearch { /** * @param args */ public static void main(String[] args) { // 注意(0<=x<n && 0<=y<m) int n = 5; int m = 4; int x = 0; int y = 0; RecollectionSearch rs = new RecollectionSearch(n, m, x, y); rs.find(x, y, 2); System.out.println("######################"); System.out.println("总解数count=" + rs.getCount()); System.out.println("######################"); } // 棋盘行数 private int n; // 棋盘列数 private int m; // 马的起始x坐标 private int x; // 马的起始y坐标 private int y; // 棋盘坐标 private int[][] a; // 求解总数 private int count; // "日"子x坐标 public int[] fx = { 1, 2, 2, 1, -1, -2, -2, -1 }; // "日"子y坐标 public int[] fy = { 2, 1, -1, -2, -2, -1, 1, 2 }; /** * 构造方法 * * @param _n * @param _m * @param _x * @param _y */ public RecollectionSearch(int _n, int _m, int _x, int _y) { n = _n; m = _m; x = _x; y = _y; a = new int[n][m]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { a[i][j] = 0; } } // 马的起点 a[x][y] = 1; count = 0; } public void find(int x, int y, int dep) { int i, xx, yy; for (i = 0; i < 7; i++) { xx = x + fx[i]; yy = y + fy[i]; // 判断新坐标是否出界,是否已走过 if (check(xx, yy) == 1) { a[xx][yy] = dep; if (dep == n * m) { output(); } else { // 从新坐标出发,递归下一层 find(xx, yy, dep + 1); } // 回溯,恢复未走标志 a[xx][yy] = 0; } } } /** * 判断新坐标是否出界,是否已走过 * * @param xx * @param yy * @return */ public int check(int xx, int yy) { if (xx >= n || yy >= m || xx < 0 || yy < 0 || a[xx][yy] != 0) { return 0; } return 1; } /** * 输出结果 */ public void output() { count++; System.out.println("count=" + count); for (int y = 0; y < n; y++) { System.out.println(""); for (int x = 0; x < m; x++) { System.out.print(a[y][x] + " "); } } System.out.println(""); } public int getN() { return n; } public void setN(int n) { this.n = n; } public int getM() { return m; } public void setM(int m) { this.m = m; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-23
第72行代码 for (i = 0; i < 7; i++) {
这里的i < 8才对吧?不是有8个方向吗? |
|
返回顶楼 | |
发表时间:2010-05-24
crazysky 写道 第72行代码 for (i = 0; i < 7; i++) {
这里的i < 8才对吧?不是有8个方向吗? 恩~~ 您说的对 应该是第72行代码 for (i = 0; i < 8; i++) { 谢谢 |
|
返回顶楼 | |