关于棋盘马走“日”字问题的回溯算法实现

1. 回溯法基本思想

   回溯法是在包含问题的所有解得解空间树(或森林)中,按照深度优先的策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任一结点时,总是先判断该结点是否满足问题的约束条件。如果满足进入该子树,继续
按照深度优先的策略进行搜索。否则,不去搜索以该结点为根的子树,而是逐层向其祖先结点回溯。

   回溯法在用来求解问题的所有解时,要回溯到根,且根结点的所有可行的子树都已被搜索遍才结束。而回溯法
在用来求解问题的任一解时,只要搜索到问题的一个解就可以结束。适用于解决一些最优化问题。

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代码实现

Java代码 复制代码
  1. package test;   
  2.   
  3. /**  
  4.  * create on 2010.05.21 TODO 回溯算法  
  5.  *   
  6.  * @author 毛正吉  
  7.  * @version v1.0  
  8.  *   
  9.  */  
  10. public class RecollectionSearch {   
  11.   
  12.     /**  
  13.      * @param args  
  14.      */  
  15.     public static void main(String[] args) {   
  16.         // 注意(0<=x<n && 0<=y<m)   
  17.         int n = 5;   
  18.         int m = 4;   
  19.         int x = 0;   
  20.         int y = 0;   
  21.         RecollectionSearch rs = new RecollectionSearch(n, m, x, y);   
  22.         rs.find(x, y, 2);   
  23.         System.out.println("######################");   
  24.         System.out.println("总解数count=" + rs.getCount());   
  25.         System.out.println("######################");   
  26.   
  27.     }   
  28.   
  29.     // 棋盘行数   
  30.     private int n;   
  31.     // 棋盘列数   
  32.     private int m;   
  33.     // 马的起始x坐标   
  34.     private int x;   
  35.     // 马的起始y坐标   
  36.     private int y;   
  37.     // 棋盘坐标   
  38.     private int[][] a;   
  39.     // 求解总数   
  40.     private int count;   
  41.     // "日"子x坐标   
  42.     public int[] fx = { 1221, -1, -2, -2, -1 };   
  43.     // "日"子y坐标   
  44.     public int[] fy = { 21, -1, -2, -2, -112 };   
  45.   
  46.     /**  
  47.      * 构造方法  
  48.      *   
  49.      * @param _n  
  50.      * @param _m  
  51.      * @param _x  
  52.      * @param _y  
  53.      */  
  54.     public RecollectionSearch(int _n, int _m, int _x, int _y) {   
  55.         n = _n;   
  56.         m = _m;   
  57.         x = _x;   
  58.         y = _y;   
  59.         a = new int[n][m];   
  60.         for (int i = 0; i < n; i++) {   
  61.             for (int j = 0; j < m; j++) {   
  62.                 a[i][j] = 0;   
  63.             }   
  64.         }   
  65.         // 马的起点   
  66.         a[x][y] = 1;   
  67.         count = 0;   
  68.     }   
  69.   
  70.     public void find(int x, int y, int dep) {   
  71.         int i, xx, yy;   
  72.         for (i = 0; i < 7; i++) {   
  73.             xx = x + fx[i];   
  74.             yy = y + fy[i];   
  75.             // 判断新坐标是否出界,是否已走过   
  76.             if (check(xx, yy) == 1) {   
  77.                 a[xx][yy] = dep;   
  78.                 if (dep == n * m) {   
  79.                     output();   
  80.                 } else {   
  81.                     // 从新坐标出发,递归下一层   
  82.                     find(xx, yy, dep + 1);   
  83.                 }   
  84.                 // 回溯,恢复未走标志   
  85.                 a[xx][yy] = 0;   
  86.             }   
  87.         }   
  88.     }   
  89.   
  90.     /**  
  91.      * 判断新坐标是否出界,是否已走过  
  92.      *   
  93.      * @param xx  
  94.      * @param yy  
  95.      * @return  
  96.      */  
  97.     public int check(int xx, int yy) {   
  98.         if (xx >= n || yy >= m || xx < 0 || yy < 0 || a[xx][yy] != 0) {   
  99.             return 0;   
  100.         }   
  101.         return 1;   
  102.     }   
  103.   
  104.     /**  
  105.      * 输出结果  
  106.      */  
  107.     public void output() {   
  108.         count++;   
  109.         System.out.println("count=" + count);   
  110.         for (int y = 0; y < n; y++) {   
  111.             System.out.println("");   
  112.             for (int x = 0; x < m; x++) {   
  113.                 System.out.print(a[y][x] + " ");   
  114.             }   
  115.         }   
  116.         System.out.println("");   
  117.     }   
  118.   
  119.     public int getN() {   
  120.         return n;   
  121.     }   
  122.   
  123.     public void setN(int n) {   
  124.         this.n = n;   
  125.     }   
  126.   
  127.     public int getM() {   
  128.         return m;   
  129.     }   
  130.   
  131.     public void setM(int m) {   
  132.         this.m = m;   
  133.     }   
  134.   
  135.     public int getCount() {   
  136.         return count;   
  137.     }   
  138.   
  139.     public void setCount(int count) {   
  140.         this.count = count;   
  141.     }   
  142.   
  143.     public int getX() {   
  144.         return x;   
  145.     }   
  146.   
  147.     public void setX(int x) {   
  148.         this.x = x;   
  149.     }   
  150.   
  151.     public int getY() {   
  152.         return y;   
  153.     }   
  154.   
  155.     public void setY(int y) {   
  156.         this.y = y;   
  157.     }   
  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值