一、问题

马踏棋盘问题,又称骑士漫步、,它是一个非常有趣的智力问题。马踏棋盘问题的大意如下:

国际象棋的棋盘有8行8列共64个单元格,无论将马放于棋盘的哪个单元格,都可让马踏遍棋盘的每个单元格。问马应该怎么走才可以踏遍棋盘的每个单元格?


算法-经典趣题-马踏棋盘(又称骑士周游)_Java​  


二、分析

我们来分析一下马踏棋盘问题。在国际象棋中,马只能走“日”字形,但是马位于不同的位置其可以走的方向有所区别:

当马位于棋盘中间位置时,马可以向8个方向跳动;

当马位于棋盘的边或角时,马可以跳动的方向将少于8个。

另外,为了求解最少的走法,当马所跳向的8个方向中的某一个或几个方向已被马走过,那么马也将跳至下一步要走的位置。可以使用递归的思想来解决马踏棋盘问题。

我们可以使用递归的思想来解决马踏棋盘问题,其操作步骤如下:

(1)从起始点开始向下一个可走的位置走一步。

(2)接着以该位置为起始,再向下一个可走的位置走一步。

(3)这样不断递归调用,直到走完64格单元格,就找到一个行走方案。

这里需要注意的是,如果在行走过程中,某个位置向8个方向都没有可走的点,则需要退回上一步,从上一个位置的另外一个可走位置继续递归调用,直至找到一个行走方案。

三、编程

package com.joshua317;

import java.util.Arrays;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
int i, j;
Coordinate start = new Coordinate(0, 0);
System.out.println("马踏棋盘问题");
System.out.println("请输入马的一个其实位置(x,y)");
Scanner scanner = new Scanner(System.in);
start.x = scanner.nextInt();
start.y = scanner.nextInt();
if (start.x < 1 || start.x > 8 || start.y < 1 || start.y > 8) {
System.out.println("起始位置输入有误!END!!!");
} else {
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
HorseChessBoard.chessBoard[i][j] = 0;
}
}

start.x--;
start.y--;
HorseChessBoard.curstep = 1;
HorseChessBoard.move(start);
}
}
}

class Coordinate {
int x,y;
public Coordinate (int a, int b) {
x = a;
y = b;
}
}
class HorseChessBoard {
static int[][] chessBoard = new int[8][8];
static int curstep;
//马可以走的8个方向
static Coordinate[] direction = {
new Coordinate(-2, 1),new Coordinate(-1, 2),new Coordinate(1, 2),new Coordinate(2, 1),
new Coordinate(2, -1),new Coordinate(1, -2),new Coordinate(-1, -2),new Coordinate(-2, -1)
};

public static void move(Coordinate curpos)
{
Coordinate next = new Coordinate(0, 0);
int i, j;
//判断是否越界
if (curpos.x < 0 || curpos.x > 7 || curpos.y < 0 || curpos.y > 7) {
return;
}
//判断是否走过
if (chessBoard[curpos.x][curpos.y] != 0) {
return;
}
//保存步数
chessBoard[curpos.x][curpos.y] = curstep;
curstep++;
//如果棋盘位置都走完
if (curstep > 64) {
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
System.out.printf("%5d", chessBoard[i][j]);
}
System.out.println("");
}
System.exit(0);
} else {
//8个可能的方向
for (i = 0; i < 8; i++) {
next.x = curpos.x + direction[i].x;
next.y = curpos.y + direction[i].y;
if (next.x < 0 || next.x > 7 || next.y < 0 || next.y > 7) {

} else {
move(next);
}
}
}
//清除步数序号
chessBoard[curpos.x][curpos.y] = 0;
curstep--;
}
}


执行该程序,输入马的一个起始位置(1,1),得到的结果如下图所示。


算法-经典趣题-马踏棋盘(又称骑士周游)_Algorithm_02​  


如果输入马的另外一个起始位置(8,8),得到的结果如下图所示。

算法-经典趣题-马踏棋盘(又称骑士周游)_深度优先搜索_03​  


四、扩展

马踏棋盘是经典的程序设计问题之一,主要的解决方案有两种:

一种是基于深度优先搜索的方法,另一种是基于贪婪算法的方法。

第一种基于深度优先搜索的方法是比较常用的算法,深度优先搜索算法也是数据结构中的经典算法之一,主要是采用递归的思想,一级一级的寻找,最后找到合适的解。

而基于贪婪的算法则是依据贪婪算法的思想设置一种标准,然后依据标准进行选择,从而得到解,但是他不一定能够得到最优解。

深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

基于深度优先搜索的算法就是依据当前点找到下一个可能的点,然后对这个点进行深度优先搜索,然后依次递归,当出现条件不满足时,退回来,采用其他的路劲进行搜索,最后肯定能够得到对应的结果。

五、通过贪心算法实现

采用贪心算法,对路径有目的地筛选,尽量选择出口少的路先走,也就是对当前点的下一个落脚点(可能是8个)进行排序,优先走可走的路最少的那个点,使得走法较好。通俗来讲,就是先预判下一个可能落脚点的出口数,出口数最少的先走掉。