骑士游历算法

1 骑士游历问题

  在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次。若给定起始位置(x0,y0),编程探索出一条路径,沿着这条路径马能遍历棋盘上的所有单元格。

  设当前马在棋盘的某个位置(x,y)上,按照规则,下一步有8个方向可走。

  设二维数组mat表示棋盘,每个元素表示棋盘的一格,其值定义如下:

  0表示马未到达过

  Mat[i,j]= -1表示棋盘边界

  自然数 表示马到达该格的步数

2 常规的回溯算法

  2.1 设计思想

  马从棋盘上的某一初始位置(x0,y0)开始,每次选择一个方向k,向前走一格,直到走完64格为止。每走一格,设置数组中相应格的元素值为马走的步数。

  如果选择的方向k=0,表示可能的8种走向都试探不通,不通的原因是走向超出了棋盘范围,或当前位置已经被马访问过。此时马已无路可走,说明前几步走得不对,应该退回去重新换一种走法,这种逐步试探的设计思想称为回溯算法。

  2.2 性能评价

  回溯算法在每一格上朝一个方向盲目地进行试探,遇到在某一格上所有方向都不能走通时,才回到前一格上来试探另一个方向。当每一格上的所有方向都试探过,不能走通时,才做出“走不通”的结论。因此该算法在探索时带有一定的盲目性和随机性,运行效率较低。

3 预见算法

  3.1 设计思想

  回溯算法的思路是可行的,但它的运行效率较低,原因在于每步试探的随机性和盲目性。如果能够找到一种克服这种随机性和盲目性的办法,按照一定规律选择前进的方向,则将增加成功的可能性,运行时间也大为缩短。本文提出的算法在这方面有所突破。

  如果在每步选择方向时,不是任意选择一个方向,而是经过一定的测试和计算,“预见”每条路的“宽窄”,再选择一条最“窄”的路先走,成功的可能性较大。理由是先走“困难的路”,光明大道留在后面。因为每一格迟早都要走到,与其把困难留在后面,不如先走“困难的路”,这样路就会越走越宽,成功的机会就越大。这种方法称为预见算法。

  3.2 实现手段

  为每个方向测定一个值――可通路数,它表示该位置上还有多少条通路。在每一格上对8个方向都进行试探,并分析比较,下一步应该选择可通路数值最小的方向走。


下面表格是有多少个位置可以到达当前这个位置:

23444432
34666643
46888864
46888864
46888864
46888864
34666643
23444432

下面上代码

public class Knight {
    
    private boolean Travel(int firstX, int firstY, int[][] board) {

        // 对应骑士可走的8个方向
        int[] movex = { -2, -1, 1, 2,  2,  1, -1, -2 };
        int[] movey = {  1,  2, 2, 1, -1, -2, -2, -1 };

        // 下一步出路的位置
        int[] nextStepX = new int[board.length];
        int[] nextStepY = new int[board.length];

        // 记录出路的个数
        int[] exitS = new int[board.length];
        int nextX = firstX;
        int nextY = firstY;
        board[nextX][nextY] = 1;

        for (int m = 2; m <= Math.pow(board.length, 2); m++) {
            
            //初始化下一个位置可走的位置的数目
            for (int i = 0; i < board.length; i++) {
                exitS[i] = 0;
            }
            
            int count = 0;
            // 试探8个方向
            for (int i = 0; i < 8; i++) {
                int temI = nextX + movex[i];
                int temJ = nextY + movey[i];

                // 走到边界,路断
                if (temI < 0 || temI > 7 || temJ < 0 || temJ > 7) {
                    continue;
                }

                // 记录下可走的方向
                if (0 == board[temI][temJ]) {
                    nextStepX[count] = temI;
                    nextStepY[count] = temJ;
                    count++;
                }
            }

            // 到这里,cout表示当前点有几种走法。nextStep中存储各种走法的坐标。
            int min = -1;
            if (count == 0) {
                return false;
            }

            if (1 == count) {
                min = 0;
            } else {// 这一步是为了找到下一次走法中最少种走法的那一步
                for (int i = 0; i < count; i++) {
                    for (int j = 0; j < 8; j++) {
                        int temI = nextStepX[i] + movex[j];
                        int temJ = nextStepY[i] + movey[j];
                        if (temI < 0 || temI > 7 || temJ < 0 || temJ > 7) {
                            continue;
                        }
                        
                        // 记录下这个位置可走的方向数
                        if (0 == board[temI][temJ]) {
                            exitS[i]++;
                        }
                    }
                }

                int tem = exitS[0];
                min = 0;

                // 从可走的方向中,寻找最少走的出路
                for (int i = 1; i < count; i++) {
                    if (tem > exitS[i]) {
                        tem = exitS[i];
                        min = i;
                    }
                }
            }

            // 得到最少的出路
            nextX = nextStepX[min];
            nextY = nextStepY[min];
            board[nextX][nextY] = m;
        }

        return true;
    }

    public static void main(String[] args) {

        int firstX, firstY;
        System.out.println("输入起始点(0-7):");
        Scanner scanner = new Scanner(System.in);

        firstX = scanner.nextInt();
        firstY = scanner.nextInt();
        int[][] board = new int[8][8];
        Knight knight = new Knight();

        if (knight.Travel(firstX, firstY, board)) {
            System.out.println("游历完成:");
        } else {
            System.out.println("游历失败!\n");
        }

        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] < 10) {
                    System.out.print(" " + board[i][j]);
                } else {
                    System.out.print(board[i][j]);
                }
                System.out.print(" ");
            }
            System.out.println();
        }
    }
}


参看文献:

http://www.xzbu.com/8/view-1086965.htm

http://www.wuji8.com/meta/266880944.html

http://wenku.baidu.com/link?url=kDUHpgB1ZPtk_CDE8l0q_ekArNkRiOPMHBb-YDcNsupACwajBJt2jm3ySyR2D6b7llZdVhnXVRiGhmdfP7-uNUQ7MuaUO3lFZUhFJ4TkE4C

  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值