1 2

骑士巡游问题

问题来源

公元9世纪的印度,一位智者研究了象棋棋子马不重复走遍所有方格的问题,这就是后来著名的骑士巡游问题。

关键信息
1.骑士按L型走,即象棋中马的走法
2.不重复走相同方格
3.走遍所有方格

例如在一个8x8的方格上,骑士位于坐标为(4,4)的点,那么骑士可达的点为灰色方格1~8
骑士巡游方式示例
骑士可以走的方向最多有八个,分别为向x轴移动1、1、-1、-1、2、2、-2、-2个单位同时向y轴移动2、-2、2、-2、1、-1、1、-1个单位

即在x、y轴上移动、一个方向移动2个单位,另一个方向移动1个单位。故共有八种组合情况

在8x8的方格上各点骑士可选择的方向的数量为各点的数字
8X8方格上各点骑士巡游方向可供选择的数量

利用回溯法求解的关键在于剪枝策略的设计

  1. 已经到达过的点或者超出棋盘范围的点不遍历
  2. 先遍历可达点数较少的点即较难到达的点
    利用上述两个条件进行剪枝
C++代码
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;

const int SIZE = 8;

// 检测当前选择的位置是否有效
bool vaildWay(int col, int row, int board[][SIZE]) {
    return (col >= 0 && col < SIZE&& row >= 0
        && row < SIZE && !board[row][col]);
}

int main() {
    int board[SIZE][SIZE] = { 0 };      // 初始化棋盘数组
    int access[SIZE][SIZE] = {    2, 3, 4, 4, 4, 4, 3, 2,
                                  3, 4, 6, 6, 6, 6, 4, 3,
                                  4, 6, 8, 8, 8, 8, 6, 4,
                                  4, 6, 8, 8, 8, 8, 6, 4,
                                  4, 6, 8, 8, 8, 8, 6, 4,
                                  4, 6, 8, 8, 8, 8, 6, 4,
                                  3, 4, 6, 6, 6, 6, 4, 3,
                                  2, 3, 4, 4, 4, 4, 3, 2 }; // 可达性数组
    int horizontal[SIZE] = { 2, 1, -1, -2, -2, -1, 1, 2 };  // 水平位移
    int vertical[SIZE] = { -1, -2, -2, -1, 1, 2, 2, 1 };    // 垂直位移
    int currentCol, currentRow;     // 当前位置
    int testCol, testRow;           // 测试位置
    int moveSteps = 0;              // 移动步伐
    cout << "请输入初始点的坐标:" << endl;     //输入初始点的位置
    cin >> currentCol;
    cin >> currentRow;
    board[currentRow][currentCol] = ++moveSteps;    // 标记起始位置
    bool done = false;

    while (!done) {
        int miniWay = 9;        // 挑选最小的可达性位置
        int direction = -1;     // 记录方向
        for (int i = 0; i < SIZE; ++i) {        // 扫描8个方向
            testCol = currentCol + horizontal[i];
            testRow = currentRow + vertical[i];
            if (vaildWay(testCol, testRow, board)) {
                if (access[testRow][testCol] < miniWay) {
                    miniWay = access[testRow][testCol];
                    direction = i;
                }
                --access[testRow][testCol]; // 更新可达性数组
            }
        }

        if (direction == -1)    // 如果没有合适的方向
            done = true;
        else {                  // 更新当前位置
            currentCol += horizontal[direction];
            currentRow += vertical[direction];
            board[currentRow][currentCol] = ++moveSteps;
        }
    }

    if (moveSteps == 64)        // 如果遍历到所有的方格位置
        cout << "   巡游路径\n\n";
    else
        cout << "   未找到满足条件的巡游路径\n\n";

    for (int i = 0; i < SIZE; ++i) {    // 输出棋盘数据
        for (int j = 0; j < SIZE; ++j)
            cout << setw(3) << board[i][j];
        cout << endl;
    }

    return 0;
}
posted @ 2021-12-03 10:22  东南海风  阅读(867)  评论(0)    收藏  举报