骑士巡游问题

Posted southerneast

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了骑士巡游问题相关的知识,希望对你有一定的参考价值。

问题概述

骑士巡游问题对国际象棋爱好者来说是较有意思的难题之一。这个问题是:称为骑士的棋子在一个空的棋盘上行进,能否在64个方格棋盘上的每个方格都走一次且只走一次。

在国际象棋中,骑士的移动路线是L形的(在一个方向上走两格,在垂直方向上走一格)。因此在一个空棋盘中间的方格上,其实可以有8种不同的移动方式(从0到7编号),如下图所示:
技术图片

可达性试探法

算法思想

根据每个方格的可到达程度将它们分类,然后总是把骑士移动到最难到达的那个方格(当然,要符合骑士的L形移动规则)。我们给一个二维array对象access填上数,这些数表示每个方格周围有多少个可到达的方格。在一个空棋盘上,每个中心方格定为8,每个角落定为2,其他的方格为3、4或6,如下所示:
技术图片
在任何时候,骑士都应该移动到具有最低可达数的方格。如果满足此条件的方格不止一个,骑士可以选择移动到其中的任何一个方格。因此,骑士巡游可以从任何一个角落开始。需要注意的是:随着骑士在棋盘上的移动,越来越多的方格被占用,因此应该随之减少可达数。
这样,在巡游的任何时刻,每个有效方格的可达数与该方格可到达的确切方格数保持相等。

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;              // 移动步伐
    srand(time(0));
    currentCol = rand() % 8;        // 随机选择起始位置
    currentRow = rand() % 8;
    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 << "   successful!!

";
    else
        cout << "   failed

";

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

	return 0;
}

以上是关于骑士巡游问题的主要内容,如果未能解决你的问题,请参考以下文章

069.骑士巡游

骑士巡游的问题简述如下:在国际象棋盘上某一位置放置一个马的棋子,然后采用象棋中“马走日字”规则

每天刷个算法题20160523:骑士巡游的递归转非递归解法

. 编写程序求解骑士巡游问题:在n行n列的棋盘上(如n=5),假设一位骑士(按象棋中“马走日”的行走法)从

每天刷个算法题20160523:骑士巡游的递归转非递归解法

每天刷个算法题20160523:骑士巡游的递归转非递归解法