马踏棋盘算法(Java版),带讲解

马踏棋盘算法(Java版)-- 带讲解

规则介绍

一道著名的谜题。

象棋中的马的行走方式是斜做走,即向一个方向移动一格后再向垂直方向移动两格为最终落子点,或向一个方向移动两格后再向垂直方向移动一格为最终落子点,如下图,图中白马可向有马轮廓的位置移动。
在这里插入图片描述
算法的规则是:马已经落子的格子不能再次落子,即下图 1标识的格不能在落子,在以上规则的前提下将所有棋盘的格子落子一次,这就是马踏棋盘。

算法思路

运用递归算法和回溯算法实现,具体思路如下图
在这里插入图片描述

代码实现

代码如下

    /** 棋盘 */
    private static int[][] checkerboard ;
    /** 行走轨迹 */
    private static List<int[]> historyStep = new LinkedList<>();
    /** 行走最大轨迹 */
    private static List<int[]> maxHistoryStep = new LinkedList<>();
    /** 小脚 */
    private static final int SMALL_STEPS = 1;
    /** 大脚 */
    private static final int BIG_STEPS = 2;
    /** 能落子的最大步数 */
    private static int maxStep;

    /** 新建棋盘 */
    private static void createCheckerboard(int row){
        checkerboard = new int[row][row];
        maxStep = row*row;
    }

    /** 向左上左移动 */
    private static int[] liftUpLiftMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] - BIG_STEPS,stepBefore[1] - SMALL_STEPS};
        // x/y小于0表示出界,棋盘上对应的点不等于0表示已落过子
        if ( stepAfter[0] < 0 || stepAfter[1] < 0 ){
            return null;
        }
        return stepAfter;
    }
    /** 向左上右移动 */
    private static int[] liftUpRightMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] - SMALL_STEPS,stepBefore[1] - BIG_STEPS};
        if ( stepAfter[0] < 0 || stepAfter[1] < 0 ){
            return null;
        }
        return stepAfter;
    }
    /** 向右上左移动 */
    private static int[] rightUpLiftMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] + SMALL_STEPS,stepBefore[1] - BIG_STEPS};
        // x大于等于棋盘格数或y小于0表示出界,棋盘上对应的点不等于0表示已落过子
        if ( stepAfter[0] >= checkerboard.length || stepAfter[1] < 0 ){
            return null;
        }
        return stepAfter;
    }
    /** 向右上右移动 */
    private static int[] rightUpRightMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] + BIG_STEPS,stepBefore[1] - SMALL_STEPS};
        if ( stepAfter[0] >= checkerboard.length || stepAfter[1] < 0 ){
            return null;
        }
        return stepAfter;
    }
    /** 向左下左移动 */
    private static int[] liftDownLiftMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] - BIG_STEPS,stepBefore[1] + SMALL_STEPS};
        if ( stepAfter[0] < 0 || stepAfter[1] >= checkerboard.length ){
            return null;
        }
        return stepAfter;
    }
    /** 向左下右移动 */
    private static int[] liftDownRightMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] - SMALL_STEPS,stepBefore[1] + BIG_STEPS};
        if ( stepAfter[0] < 0 || stepAfter[1] >= checkerboard.length){
            return null;
        }
        return stepAfter;
    }
    /** 向右下左移动 */
    private static int[] rightDownLiftMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] + SMALL_STEPS,stepBefore[1] + BIG_STEPS};
        if ( stepAfter[0] >= checkerboard.length || stepAfter[1] >=checkerboard.length){
            return null;
        }
        return stepAfter;
    }
    /** 向右下右移动 */
    private static int[] rightDownRightMove(int[] stepBefore){
        int[] stepAfter = {stepBefore[0] + BIG_STEPS,stepBefore[1] + SMALL_STEPS};
        if ( stepAfter[0] >= checkerboard.length || stepAfter[1] >= checkerboard.length){
            return null;
        }
        return stepAfter;
    }

    /** 计算下一步落点 */
    private static boolean nextStep(int[] drop){
        // 如果该位置已落过子,则直接退出
        if (checkerboard[drop[1]][drop[0]] != 0){
            return false;
        }
        // 记录落子位置
        historyStep.add(drop);
        checkerboard[drop[1]][drop[0]] = 1;
        // 达到最大落子数
        if (historyStep.size() == maxStep){
            maxHistoryStep.clear();
            maxHistoryStep.addAll(historyStep);
            return true;
        }
        // 记录最大落子方式
        if (historyStep.size() > maxHistoryStep.size()){
            maxHistoryStep.clear();
            maxHistoryStep.addAll(historyStep);

        }
        int [] stepNext ;
        if (!Objects.isNull( stepNext = liftUpLiftMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)) {
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = liftUpRightMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = rightUpLiftMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = rightUpRightMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = liftDownLiftMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = liftDownRightMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = rightDownLiftMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        if (!Objects.isNull( stepNext = rightDownRightMove(drop))){
            // 继续落下一步
            if (nextStep(stepNext)){
                // 如果已到最大落子数则直接返回
                return true;
            }
        }
        // 落子失败,移除记录
        historyStep.remove(historyStep.size()-1);
        checkerboard[drop[1]][drop[0]] = 0;
        soutListAllHis();
        return false;
    }

    public static void main(String[] args) {
        int lattice = 6;
        createCheckerboard(lattice);
        int xStart = (int)(Math.random()*lattice);
        int yStart = (int)(Math.random()*lattice);
        xStart = 3;
        yStart = 2;
        int [] stepStart = {xStart,yStart};
        System.out.println("Arrays.toString(stepStart) = " + Arrays.toString(stepStart));
        nextStep(stepStart);
        soutListAll();
    }

    private static void soutListAll() {
        System.out.println("maxHistoryStep.size() = " + maxHistoryStep.size());
        maxHistoryStep.forEach(a-> System.out.print(Arrays.toString(a) + ","));
        System.out.println();
    }

实现效果

如下是行走路径,棋盘从左至有是x轴方向,从上到下是y轴方向,[0,0]表示右上角的第一格子
[3, 2],[1, 1],[3, 0],[2, 2],[0, 1],[2, 0],[4, 1],[5, 3],[4, 5],[3, 3],[1, 4],[0, 2],[1, 0],[3, 1],[5, 0],[4, 2],[5, 4],[3, 5],[2, 3],[0, 4],[1, 2],[0, 0],[2, 1],[4, 0],[5, 2],[4, 4],[2, 5],[1, 3],[0, 5],[2, 4],[0, 3],[1, 5],[3, 4],[5, 5],[4, 3],[5, 1]

行走路线如下图 ,五角星是开始所在位置
在这里插入图片描述

温馨提示

计算时,棋盘不要太大,已不能太小(不能小于3),棋盘太多计算速度非常慢,以6*6的棋盘为例,假如最终解法的落子方式都是在行走的最后一种方式(这种方式不可能存在),则需计算836次落子方式,即2108次落子方式,这个数量级有段恐怖就过多解释了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值