马踏棋盘算法
概述
- 马踏棋盘算法也被称为骑士周游问题, 规则是将马随机放在国际象棋的8x8棋盘(board[07][07])的某个方格中, 马儿走棋规则(马走日字)进行移动, 且每个方格只进入一次, 走遍所有方格
- 代码思路
- 深度优先搜索(Depth First Search): 每当走到尽头未走完所有方格, 便会回退, 也就是会发生大量的回溯
- 使用贪心算法(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
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!