#include
#include
namespace backtracking {
namespace knight_tour {
// 函数定义
}
}
头文件:
用于数组操作,
用于输入输出。
命名空间:将代码逻辑组织到 backtracking::knight_tour
中,提高模块化。
issafe
template
bool issafe(int x, int y, const std::array, V>& sol) {
return (x < V && x >= 0 && y < V && y >= 0 && sol[x][y] == -1);
}
功能:检查坐标 (x, y)
是否在棋盘范围内且未被访问过。
参数:
V
:棋盘大小(如8x8)。
x, y
:待检查的位置。
sol
:棋盘状态,-1
表示未访问。
返回值:合法返回 true
,否则 false
。
solve
template
bool solve(int x, int y, int mov, std::array, V>& sol,
const std::array& xmov, std::array& ymov) {
// 终止条件:所有格子已访问
if (mov == V * V) return true;
// 尝试所有8种移动方向
for (int k = 0; k < V; k++) {
int xnext = x + xmov[k];
int ynext = y + ymov[k];
if (issafe(xnext, ynext, sol)) {
sol[xnext][ynext] = mov; // 标记当前位置
// 递归尝试下一步
if (solve(xnext, ynext, mov + 1, sol, xmov, ymov)) {
return true; // 找到解,提前返回
} else {
sol[xnext][ynext] = -1; // 回溯,撤销当前标记
}
}
}
return false; // 所有方向均无解
}
功能:递归尝试所有可能的移动路径,找到骑士周游问题的解。
参数:
x, y
:当前位置。
mov
:当前步数(从0开始)。
sol
:棋盘状态,记录每一步的位置。
xmov, ymov
:骑士的8种移动方向。
流程:
终止条件:当 mov
等于棋盘格子总数时,所有格子已访问,返回 true
。
遍历所有移动方向,计算下一步坐标 (xnext, ynext)
。
安全检查:若下一步合法,则标记该位置为当前步数。
递归探索:进入下一步(mov + 1
),若递归返回 true
,表示找到解,逐层返回。
回溯:若递归未找到解,撤销当前标记,尝试其他方向。
无解:所有方向尝试完毕仍无解,返回 false
。
main
int main() {
const int n = 8;
std::array, n> sol;
// 初始化棋盘为-1(未访问)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
sol[i][j] = -1;
}
}
// 定义骑士的8种移动方向(相对坐标)
std::array xmov = {2, 1, -1, -2, -2, -1, 1, 2};
std::array ymov = {1, 2, 2, 1, -1, -2, -2, -1};
sol[0][0] = 0; // 起始位置
// 调用求解函数
bool flag = backtracking::knight_tour::solve(0, 0, 1, sol, xmov, ymov);
// 输出结果
if (!flag) {
std::cout << "Solution does not exist!\n";
} else {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
std::cout << sol[i][j] << "\t";
}
std::cout << "\n";
}
}
return 0;
}
流程:
初始化棋盘:所有位置设为 -1
,表示未访问。
定义移动方向:骑士的8种可能移动(如 (2, 1)
表示横向移动2格,纵向1格)。
设置起始点:sol[0][0] = 0
表示从左上角开始。
调用求解函数:从 (0,0)
开始,步数为1。
输出结果:若找到解,打印棋盘每一步的位置;否则提示无解。
回溯策略:通过递归尝试所有可能的路径,遇到死胡同时回溯,撤销最后一步并尝试其他方向。
时间复杂度:最坏情况下为指数级(O(8^(N^2))),但实际中通过剪枝可大幅优化。
空间复杂度:O(N^2) 用于存储棋盘状态。
对于8x8棋盘,输出为一个8x8矩阵,每个元素表示骑士在第几步访问该位置。例如:
0 59 38 33 30 17 8 63
37 34 31 60 9 62 29 16
58 1 36 39 32 27 18 7
35 48 41 26 61 10 15 28
42 57 2 49 40 23 6 19
47 50 45 54 25 20 11 14
56 43 52 3 22 13 24 5
51 46 55 44 53 4 21 12
验证:每个数字从0到63连续,相邻数字间符合骑士移动规则。
该代码通过回溯法系统地探索所有可能的骑士路径,确保找到解(如果存在)。虽然时间复杂度较高,但对于标准8x8棋盘仍能有效求解。