算法-马踏棋盘算法

马踏棋盘算法

概述

  • 马踏棋盘算法也被称为骑士周游问题, 规则是将马随机放在国际象棋的8x8棋盘(board[07][07])的某个方格中, 马儿走棋规则(马走日字)进行移动, 且每个方格只进入一次, 走遍所有方格

在这里插入图片描述

  • 代码思路
  1. 深度优先搜索(Depth First Search): 每当走到尽头未走完所有方格, 便会回退, 也就是会发生大量的回溯
  2. 使用贪心算法(Greedy algorithm), 优化深度优先搜索时产生的回溯
  • 代码实现

public class HorseChessboardApp {
    /** 棋盘的列数*/
    private static int X = 8;
    /** 棋盘的行数*/
    private static int Y = 8;
    /** 棋盘的各个方格, 标记是否被访问过*/
    private static boolean visited[] = new boolean[X * Y];
    /** 棋盘的所有位置是否都已被访问, true表示成功*/
    private static boolean finished;

    public static void main(String[] args) {
        /** 定义棋盘*/
        int[][] chessboard = new int[X][Y];
        /** 马儿初始位置的行*/
        int row = 1;
        /** 马儿初始位置的列*/
        int column = 1;

        long start = System.currentTimeMillis();
        traversalChessboard(chessboard, row - 1, column - 1, 1);
        long end = System.currentTimeMillis();
        System.out.println("共耗时: " + (end - start) + " 毫秒");

        /** 打印结果*/
        for(int[] rows: chessboard) {
            for(int step: rows) {
                System.out.print(step + "\t");
            }
            System.out.println();
        }
    }

    /** 骑士周游问题的算法
     * @param chessboard 棋盘
     * @param row 马儿当前的位置的行
     * @param column 马儿当前的位置的列
     * @param step 是第几步, 初始位置就是第1步*/
    public static void traversalChessboard(int[][] chessboard, int row, int column, int step) {
        chessboard[row][column] = step;
        /** 标记该位置已经访问
         *  - row(4) * X(8) + column(4) = 36 */
        visited[row * X + column] = true;
        /** 获取当前位置可以走的下一个位置的集合*/
        ArrayList<Point> ps = next(new Point(column, row));
        /** 将当前可走的下一步所有方格, 进行非递减排序. 为减少回溯的次数*/
        sort(ps);
        /** 遍历 ps可以走的下一个位置的集合*/
        while(!ps.isEmpty()) {
            /** 取出下一个可以走的位置*/
            Point p = ps.remove(0);
            /** 判断该点是否已经访问过*/
            if(!visited[p.y * X + p.x]) { /** 未访问时, 递归查找下一步*/
                traversalChessboard(chessboard, p.y, p.x, step + 1);
            }
        }

        /**
         * 判断马儿是否完成了任务, 使用 step和棋盘总步数比较
         * - 没达到数量, 则表示没完成任务, 将整个棋盘置0 */
        if(step < X * Y && !finished) {
            chessboard[row][column] = 0;
            visited[row * X + column] = false;
        } else {
            finished = true;
        }
    }

    /** 根据当前位置(Point对象), 计算马儿还能走位置(Point 最多有8个位置), 并放入到集合中返回*/
    public static ArrayList<Point> next(Point curPoint) {
        ArrayList<Point> ps = new ArrayList<>(8);
        Point p = new Point();
        // 判断马儿是否可以走5号位置
        if((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y -1) >= 0) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走6号位置
        if((p.x = curPoint.x - 1) >=0 && (p.y=curPoint.y-2)>=0) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走7号位置
        if ((p.x = curPoint.x + 1) < X && (p.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走0号位置
        if ((p.x = curPoint.x + 2) < X && (p.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p));
        }
        // 判断马儿可以走1号位置
        if ((p.x = curPoint.x + 2) < X && (p.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走2号位置
        if ((p.x = curPoint.x + 1) < X && (p.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走3号位置
        if ((p.x = curPoint.x - 1) >= 0 && (p.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p));
        }
        // 判断马儿是否可以走4号位置
        if ((p.x = curPoint.x - 2) >= 0 && (p.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p));
        }
        return ps;
    }

    /**
     * 递减排列实例: 9,8,5,2,1,0
     * 递增排列实例: 1,3,4,5,7,9
     * 非递减排列实例: 1,1,3,3,5,6
     * 非递增排列实例: 9,8,8,2,2,2
     * - 将当前可走的下一步所有方格, 进行非递减排序. 为减少回溯的次数*/
    public static void sort(ArrayList<Point> ps) {
        ps.sort((o1, o2) -> {
            /** 获取 o1可走的下一步所有位置个数*/
            int count1 = next(o1).size();
            /** 获取 o2可走的下一步所有位置个数*/
            int count2 = next(o2).size();
            if (count1 < count2) {
                return -1;
            } else if (count1 > count2) {
                return 1;
            } else {
                return 0;
            }
        });
    }

}

输出:
共耗时: 214 毫秒
1	16	37	32	3	18	47	22	
38	31	2	17	48	21	4	19	
15	36	49	54	33	64	23	46	
30	39	60	35	50	53	20	5	
61	14	55	52	63	34	45	24	
40	29	62	59	56	51	6	9	
13	58	27	42	11	8	25	44	
28	41	12	57	26	43	10	7		

  • 游戏演示: http://www.4399.com/flash/146267_2.htm

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值