骑士之旅蛮力

Posted

技术标签:

【中文标题】骑士之旅蛮力【英文标题】:Knight's Tour Brute Force 【发布时间】:2018-03-28 23:44:23 【问题描述】:

我对骑士如何使用递归回溯感到迷茫。我已经尝试了多种方法,您可以看到它已经注释掉了一些尝试,但是它如何知道要回溯多远才能再次开始前进?我对递归的理解是函数每次调用自身时都会使用新参数构建堆栈帧,当它到达基本情况时,它会返回堆栈帧,在这种情况下向后移动。有人可以指出我正确的方向吗?谢谢。

#include "stdafx.h"
#include <iostream>
#include <iomanip>

using namespace std;


int GAMEBOARD[8][8] =  0 ;
int TOTAL_MOVES = 0, BAD_MOVES = 0;

bool moveKnight(int row, int col, int movNum);
void print();

int main()

    int startRow = 3, startCol = 3;
    int moveNum = 1;

    GAMEBOARD[startRow][startCol] = moveNum;
    TOTAL_MOVES++;

    moveKnight(startRow, startCol, moveNum);

    if (moveKnight(startRow, startCol, moveNum) == true) 
        cout << "Knight's Tour Solved! It took " << TOTAL_MOVES <<
            " total moves and " << BAD_MOVES << " bad moves." << endl;
    

    system("pause");
    return 0;


bool moveKnight(int row, int col, int moveNum)

    GAMEBOARD[row][col] = moveNum;
    TOTAL_MOVES++;


    if (moveNum == 64) 
        return true;
    

    if (GAMEBOARD[row][col] != 0) 
        GAMEBOARD[row][col] = 0;
        print();
        system("pause");
        return moveKnight(row, col, moveNum);
    

// commented out
    /*if (GAMEBOARD[row - 2][col + 1] != 0) 
        print();
        system("pause");
        return moveKnight(row - 2, col + 1, moveNum + 1);
    
    else if (GAMEBOARD[row - 1][col + 2] != 0) 
        print();
        system("pause");
        return moveKnight(row - 1, col + 2, moveNum + 1);
    
    else if (GAMEBOARD[row + 1][col + 2] != 0) 
        print();
        system("pause");
        return moveKnight(row + 1, col + 2, moveNum + 1);
    
    else if (GAMEBOARD[row + 2][col + 1] != 0) 
        print();
        system("pause");
        return moveKnight(row + 2, col + 1, moveNum + 1);
    
    else if (GAMEBOARD[row + 2][col - 1] != 0) 
        print();
        system("pause");
        return moveKnight(row + 2, col - 1, moveNum + 1);
    
    else if (GAMEBOARD[row + 1][col - 2] != 0) 
        print();
        system("pause");
        return moveKnight(row + 1, col - 2, moveNum + 1);
    
    else if (GAMEBOARD[row - 1][col - 2] != 0) 
        print();
        system("pause");
        return moveKnight(row - 1, col - 2, moveNum + 1);
    
    else if (GAMEBOARD[row - 2][col - 1] != 0) 
        print();
        system("pause");
        return moveKnight(row - 2, col - 1, moveNum + 1);
    

    return false;*/






    else if (row - 2 >= 0 && row - 2 <= 7 && col + 1 >= 0
        && col + 1 <= 7 && GAMEBOARD[row - 2][col + 1] == 0) 
        GAMEBOARD[row - 2][col + 1] = moveNum;
        print();
        system("pause");
        return moveKnight(row - 2, col + 1, moveNum + 1);
    
    else if (row - 1 >= 0 && row - 1 <= 7 && col + 2 >= 0
        && col + 2 <= 7 && GAMEBOARD[row - 1][col + 2] == 0) 
        GAMEBOARD[row - 1][col + 2] = moveNum;
        print();
        system("pause");
        return moveKnight(row - 1, col + 2, moveNum + 1);
    
    else if (row + 1 >= 0 && row + 1 <= 7 && col + 2 >= 0
        && col + 2 <= 7 && GAMEBOARD[row + 1][col + 2] == 0) 
        GAMEBOARD[row + 1][col + 2] = moveNum;
        print();
        system("pause");
        return moveKnight(row + 1, col + 2, moveNum + 1);
    
    else if (row + 2 >= 0 && row + 2 <= 7 && col + 1 >= 0
        && col + 1 <= 7 && GAMEBOARD[row + 2][col + 1] == 0) 
        GAMEBOARD[row + 2][col + 1] = moveNum;
        print();
        system("pause");
        return moveKnight(row + 2, col + 1, moveNum + 1);
    
    else if (row + 2 >= 0 && row + 2 <= 7 && col - 1 >= 0
        && col - 1 <= 7 && GAMEBOARD[row + 2][col - 1] == 0) 
        GAMEBOARD[row + 2][col - 1] = moveNum;
        print();
        system("pause");
        return moveKnight(row + 2, col - 1, moveNum + 1);
    
    else if (row + 1 >= 0 && row + 1 <= 7 && col - 2 >= 0
        && col - 2 <= 7 && GAMEBOARD[row + 1][col - 2] == 0) 
        GAMEBOARD[row + 1][col - 2] = moveNum;
        print();
        system("pause");
        return moveKnight(row + 1, col - 2, moveNum + 1);
    
    else if (row - 1 >= 0 && row - 1 <= 7 && col - 2 >= 0
        && col - 2 <= 7 && GAMEBOARD[row - 1][col - 2] == 0) 
        GAMEBOARD[row - 1][col - 2] = moveNum;
        print();
        system("pause");
        return moveKnight(row - 1, col - 2, moveNum + 1);
    
    else if (row - 2 >= 0 && row - 2 <= 7 && col - 1 >= 0
        && col - 1 <= 7 && GAMEBOARD[row - 2][col - 1] == 0) 
        GAMEBOARD[row - 2][col - 1] = moveNum;
        print();
        system("pause");
        return moveKnight(row - 2, col - 1, moveNum + 1);
    




// commented out
    /*if (row - 2 < 0 || row - 2 > 7 || col + 1 < 0
        || col + 1 > 7 || GAMEBOARD[row - 2][col + 1] != 0) 
        GAMEBOARD[row - 2][col + 1] = 0;
        return moveKnight(row - 2, col + 1, moveNum + 1);
    
    else if (row - 1 < 0 || row - 1 > 7 || col + 2 < 0
        || col + 2 > 7 || GAMEBOARD[row - 1][col + 2] != 0) 
        GAMEBOARD[row - 1][col + 2] = 0;
        return moveKnight(row - 1, col + 2, moveNum + 1);
    
    else if (row + 1 < 0 || row + 1 > 7 || col + 2 < 0
        || col + 2 > 7 || GAMEBOARD[row + 1][col + 2] != 0) 
        GAMEBOARD[row + 1][col + 2] = 0;
        moveKnight(row + 1, col + 2, moveNum + 1);
        return false;
    
    else if (row + 2 < 0 || row + 2 > 7 || col + 1 < 0
        || col + 1 > 7 || GAMEBOARD[row + 2][col + 1] != 0) 
        GAMEBOARD[row + 2][col + 1] = 0;
        moveKnight(row + 2, col + 1, moveNum + 1);
        return false;
    
    else if (row + 2 < 0 || row + 2 > 7 || col - 1 < 0
        || col - 1 > 7 || GAMEBOARD[row + 2][col - 1] != 0) 
        GAMEBOARD[row + 2][col - 1] = 0;
        moveKnight(row + 2, col - 1, moveNum + 1);
        return false;
    
    else if (row + 1 < 0 || row + 1 > 7 || col - 2 < 0
        || col - 2 > 7 || GAMEBOARD[row + 1][col - 2] != 0) 
        GAMEBOARD[row + 1][col - 2] = 0;
        moveKnight(row + 1, col - 2, moveNum + 1);
        return false;
    
    else if (row - 1 < 0 || row - 1 > 7 || col - 2 < 0
        || col - 2 > 7 || GAMEBOARD[row - 1][col - 2] != 0) 
        GAMEBOARD[row - 1][col - 2] = 0;
        moveKnight(row - 1, col - 2, moveNum + 1);
        return false;
    
    else if (row - 2 < 0 || row - 2 > 7 || col - 1 < 0
        || col - 1 > 7 || GAMEBOARD[row - 2][col - 1] != 0) 
        GAMEBOARD[row - 2][col - 1] = 0;
        moveKnight(row - 2, col - 1, moveNum + 1);
        return false;
    
        */


    cout << endl << endl;

    return false;


void print()

    for (int row = 0; row <= 7; row++) 
        for (int col = 0; col <= 7; col++) 
            cout << setw(5) << GAMEBOARD[row][col];
        
        cout << endl;
    

【问题讨论】:

en.wikipedia.org/wiki/Knight%27s_tour#Brute-force_algorithms 【参考方案1】:

递归可能很难掌握! 我强烈建议您在尝试开始编写代码之前先考虑一下您的算法将如何工作。

首先要明确定义您的基本情况。你做得很好。我们有:

if (moveNum == 64) 
    return true;

现在,我们需要考虑如何处理每个位置的骑士可能的 8 种移动。我将使用一些 sudo 代码来简化您的代码:

moves = [[2, 1], [2, 1], [-2, -1], etc...]; // 8 moves total
for (move : moves) 
    newRow = row + move[0];
    newCol = col + move[1];

    if (InBounds(newRow, newCol) && GAMEBOARD[newRow][newCol] == 0) 
        // Backtracking logic goes here
    

这段代码本质上和你的 if 语句做同样的事情,但更容易使用和概念化。

递归回溯基本上有两个步骤。首先,我们检查是否达到了基本情况。二是处理后退案件。使用递归回溯,这包括几个步骤:

    做点什么(在这种情况下,移动骑士) 进行递归调用以检查这是否会产生解决方案 如果这导致了解决方案,请告诉此函数的调用者我们找到了解决方案。此外,您可能想对找到的解决方案做一些事情。 这没有导致解决方案。撤消我们在第一步中所做的,看看其他递归情况是否会导致解决方案 如果没有递归情况导致找到解决方案,则告诉调用者从当前位置找不到解决方案。

将此格式应用于骑士之旅:

    在板上移动:GAMEBOARD[newRow][newCol] = moveNum 测试此移动是否会产生解决方案:result = moveKnight(newRow, newCol, moveNum + 1) 如果我们找到了解决方案,请暂时返回 true。 if (result) return true; 如果此移动不起作用,请撤消我们所做的移动:GAMEBOARD[newRow][newCol] = 0。然后,我们应该看看其他递归情况是否会导致解决方案。我的代码通过循环遍历每个动作来处理这个问题。您通过执行一系列 if/else 语句来处理它。 此语句中没有导致解决方案的移动。我们应该return false 表明这一点。

将所有这些放在一起,我们得到:

bool moveKnight(int row, int col, int moveNum) 

  if (moveNum == 64) 
      return true;
  
  moves = [[2, 1], [2, 1], [-2, -1], etc...]; // 8 moves total
  for (move : moves) 
    int newRow = row + move[0];
    int newCol = col + move[1];

    if (InBounds(newRow, newCol) && GAMEBOARD[newRow][newCol] == 0) 
      GAMEBOARD[newRow][newCol] = moveNum;
      bool result = moveKnight(newRow, newCol, moveNum + 1);
      if (result) 
        return true;
       else 
        // Undo this move
        GAMEBOARD[newRow][newCol] = 0;
      
    
  
  return false;

因为当我们找到正确的解决方案时我们不会撤消移动,所以当moveKnight 返回 true 时,GAMEBOARD 将包含一个解决方案。

【讨论】:

谢谢,但是这个迭代不是通过for each循环吗?递归方面在哪里?循环中的 if 语句是递归方面吗?如果是这样,怎么做?谢谢。 bool result = moveKnight(newRow, newCol, moveNum + 1); 行是递归的。正在从 moveKnight 内部调用 moveKnight

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

平行骑士之旅算法

骑士之旅中的堆栈实现[关闭]

如何解决具有特殊约束的部分骑士之旅

陷入无限循环(骑士之旅问题)

骑士之旅(回溯)

骑士之旅 - 导致无限循环,我不知道为啥