题目描述
在一个N*N 格子的棋盘上,有一只国际象棋的骑士在棋盘的左下角,骑士只能根据象棋的规则进行移动,要么横向跳动一格纵向跳动两格,要么纵向跳动一格横向跳动两格。骑士从第一个格子出发,每个格子只能访问一次,能否访问完所有的格子, 请找到一个解决方案。
题目分析
这是一道经典的回溯算法题目,这里的状态就是每一步骑士所在的坐标位置。
因为骑士可以跳动的位置总共就有8个位置,所以可以提前定义出跳动的距离数组。每一步只需要将骑士往所有方向上尝试跳动即可。
/**
* 问题描述:在一个N*N 格子的棋盘上,有一只国际象棋的骑士在棋盘的左下角,骑士只能根据象棋的规则进行移动,
* 要么横向跳动一格纵向跳动两格,要么纵向跳动一格横向跳动两格。
* 骑士从第一个格子出发,每个格子只能访问一次,能否访问完所有的格子, 请找到一个解决方案。
*/
public class KnightTour {
private int N;
private int count; // 棋盘上的结点数
private int chessBoard[][];
private int visitedNum;
private int num; // 解法数
private int solution[][]; // 存储一个任意解
private int xMove[] = new int[]{-1, 1, 2, 2, 1, -1, -2, -2};
private int yMove[] = new int[]{2, 2, 1, -1, -2, -2, -1, 1};
public int search(int N) {
this.N = N;
count = N * N;
chessBoard = new int[N][N];
visitedNum = 0;
num = 0;
solution = new int[N][N];
backtrace(0, 0);
if (num != 0)
printSolution();
return num;
}
private boolean backtrace(int x, int y) {
++visitedNum;
chessBoard[x][y] = visitedNum;
if (visitedNum == count) {
++num;
copySolution();
chessBoard[x][y] = 0;
--visitedNum;
return true;
}
boolean flag = false;
for (int i = 0; i < 8; ++i) {
int nextX = x + xMove[i];
int nextY = y + yMove[i];
if (isValid(nextX, nextY)) {
if (backtrace(nextX, nextY)) flag = true;
}
}
chessBoard[x][y] = 0;
--visitedNum;
return flag;
}
private boolean isValid(int x, int y) {
if (x >= 0 && x < N && y >= 0 && y < N && chessBoard[x][y] == 0)
return true;
return false;
}
private void printSolution() {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
System.out.print(solution[i][j] + " ");
}
System.out.println();
}
}
private void copySolution() {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j)
solution[i][j] = chessBoard[i][j];
}
}
}
这里我遍历了所有解法,N=6就很难跑出结果了。如果只是判断是否有解,可以优先选择下一步可跳路径少的方向进行剪枝操作,这里我就不再贴代码了。