我很难理解递归和回溯,尽管我做了一些简单的练习(例如斐波那契)。所以请允许我在这里介绍我的“脑流”:
我读了教科书,知道如果前一个皇后的当前位置消除了将下一个皇后放在下一列的可能性,则可以使用回溯来删除前一个皇后。所以这似乎很容易,我需要做的就是删除它并让程序决定下一个可能的位置。
过了一会儿,我发现程序停在第 6 个皇后,所以我发现如果我简单地删除第 5 个皇后,程序只需将其放回当前位置(即,给定前 4 个皇后,第 5 个皇后总是落入相同的位置)地方,这并不奇怪)。所以我认为我需要跟踪最后一个女王的位置。
这是我感到困惑的时候。如果我要跟踪最后一个皇后的位置(这样当我回溯程序时不允许将皇后放在同一个地方),有两个潜在的困难:
a) 假设我移除了第 5 个皇后,我必须编写代码来决定它的下一个可能位置。这可以通过忽略其当前位置(在被移除之前)来解决,并继续在以下行中寻找可能的位置。
b) 我应该追踪所有之前的皇后吗?似乎是这样。假设实际上我必须删除的不是一个皇后,而是两个皇后(甚至更多),我当然需要跟踪它们当前的所有位置。但这比看起来要复杂得多。
- 于是我开始在课本中寻找答案。遗憾的是它没有回溯代码,只有递归代码。然后我在这里找到了代码:
http://www.geeksforgeeks.org/backtracking-set-3-n-queen-problem/
这让我大吃一惊,因为它是如此简单,但它确实有效!唯一的回溯部分是删除最后一个女王!所以问题是:下面的代码如何确保当给定 4 个皇后的位置时,第 5 个不会一次又一次地落到同一个地方?我想我不明白的是你怎么能递归地回溯(比如说你需要删除更多的皇后)。递归前进时我没有问题,但是如何递归后退?
/* A recursive utility function to solve N Queen problem */
bool solveNQUtil(int board[N][N], int col)
{
/* base case: If all queens are placed then return true */
if (col >= N)
return true;
/* Consider this column and try placing this queen in all rows
one by one */
for (int i = 0; i < N; i++)
{
/* Check if queen can be placed on board[i][col] */
if ( isSafe(board, i, col) )
{
/* Place this queen in board[i][col] */
board[i][col] = 1;
/* recur to place rest of the queens */
if ( solveNQUtil(board, col + 1) == true )
return true;
/* If placing queen in board[i][col] doesn't lead to a solution
then remove queen from board[i][col] */
board[i][col] = 0; // BACKTRACK
}
}
/* If queen can not be place in any row in this colum col
then return false */
return false;
}
好的。现在我有一些可以工作的代码,但我主要根据上面的代码修改了我自己的代码,所以我很不稳定:
bool EightQueen(int& numQueen) {
if (numQueen == 8) {
return true;
}
if (numQueen == 0) {
PlaceQueen(0, 0);
numQueen ++;
EightQueen(numQueen);
}
int row = 0;
for (row = 0; row <= 7; row ++) {
if (CheckThis(row, numQueen)) { //Check if next queen can be put
PlaceQueen(row, numQueen); //Place next queen
numQueen ++;
DisplayQueen();
cout << endl;
if (EightQueen(numQueen)) { //Try next queen
return true;
}
ClearQueen(numQueen - 1);
numQueen --;
}
}
return false;
}
假设 numQueen 是 5,所以在 for 循环中我们将检查是否可以放置第 6 个皇后。我们知道这对所有行都是不可能的,所以该函数返回 false。我假设它然后“收缩”回它被调用的位置,即当 numQueen 为 4 时。因此调用 ClearQueen(4) 并删除最后一个皇后(第 5 个)。显然 for 循环还没有完成,所以我们将尝试下一行,看看它是否允许进一步开发。即我们检查是否可以将第 5 个皇后放在下一行,如果可以,我们将进一步检查是否可以放置第 6 个皇后,依此类推。
是的,它似乎工作,嗯,嗯,是的。