基于回溯思想的小游戏Knight‘s tour 骑士巡游

  •        骑士巡游是指在棋盘上,骑士的一系列移动,使得骑士恰好访问棋盘上的每一个方格一次。如果骑士最终停在一个与起始方格相隔一个 “马步” (象棋)的方格上(这样它就可以立即沿着相同的路径再次巡游棋盘),那么这个周游是 “闭合的”;否则,它就是 “开放的”。
  • 下面进入代码部分讲解:

1. 头文件和命名空间

#include <array>
#include <iostream>

namespace backtracking {
    namespace knight_tour {
        // 函数定义
    }
}
  • 头文件<array> 用于数组操作,<iostream> 用于输入输出。

  • 命名空间:将代码逻辑组织到 backtracking::knight_tour 中,提高模块化。

2. 安全检查函数 issafe

template <size_t V>
bool issafe(int x, int y, const std::array<std::array<int, V>, 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

3. 回溯求解函数 solve

template <size_t V>
bool solve(int x, int y, int mov, std::array<std::array<int, V>, V>& sol,
           const std::array<int, V>& xmov, std::array<int, V>& 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<V>(xnext, ynext, sol)) {
            sol[xnext][ynext] = mov; // 标记当前位置

            // 递归尝试下一步
            if (solve<V>(xnext, ynext, mov + 1, sol, xmov, ymov)) {
                return true; // 找到解,提前返回
            } else {
                sol[xnext][ynext] = -1; // 回溯,撤销当前标记
            }
        }
    }
    return false; // 所有方向均无解
}
  • 功能:递归尝试所有可能的移动路径,找到骑士周游问题的解。

  • 参数

    • x, y:当前位置。

    • mov:当前步数(从0开始)。

    • sol:棋盘状态,记录每一步的位置。

    • xmov, ymov:骑士的8种移动方向。

  • 流程

    1. 终止条件:当 mov 等于棋盘格子总数时,所有格子已访问,返回 true

    2. 遍历所有移动方向,计算下一步坐标 (xnext, ynext)

    3. 安全检查:若下一步合法,则标记该位置为当前步数。

    4. 递归探索:进入下一步(mov + 1),若递归返回 true,表示找到解,逐层返回。

    5. 回溯:若递归未找到解,撤销当前标记,尝试其他方向。

    6. 无解:所有方向尝试完毕仍无解,返回 false

4. 主函数 main

int main() {
    const int n = 8;
    std::array<std::array<int, n>, n> sol;

    // 初始化棋盘为-1(未访问)
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            sol[i][j] = -1;
        }
    }

    // 定义骑士的8种移动方向(相对坐标)
    std::array<int, n> xmov = {2, 1, -1, -2, -2, -1, 1, 2};
    std::array<int, n> ymov = {1, 2, 2, 1, -1, -2, -2, -1};

    sol[0][0] = 0; // 起始位置

    // 调用求解函数
    bool flag = backtracking::knight_tour::solve<n>(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. 初始化棋盘:所有位置设为 -1,表示未访问。

    2. 定义移动方向:骑士的8种可能移动(如 (2, 1) 表示横向移动2格,纵向1格)。

    3. 设置起始点sol[0][0] = 0 表示从左上角开始。

    4. 调用求解函数:从 (0,0) 开始,步数为1。

    5. 输出结果:若找到解,打印棋盘每一步的位置;否则提示无解。

5. 算法分析

  • 回溯策略:通过递归尝试所有可能的路径,遇到死胡同时回溯,撤销最后一步并尝试其他方向。

  • 时间复杂度:最坏情况下为指数级(O(8^(N^2))),但实际中通过剪枝可大幅优化。

  • 空间复杂度:O(N^2) 用于存储棋盘状态。

6. 示例输出

对于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棋盘仍能有效求解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值