网上八皇后问题的代码一大把,可是“回朔递归”还是理解的不好!
这里是我一步步写出的代码和详细的注释,解释了这个问题的解决用到的“回朔和递归”体现在哪里和怎么体现的两个问题,相信看了之后,对这个问题的解决原理会有深刻的理解的。
如下:
#include <iostream>
#include <stdlib.h>
using namespace std;
#define QUEUE 8 //定义皇后的个数
int queues[QUEUE];//表示一个QUEUE*QUEUE的棋盘,其值queues[i] = 第i个皇后的放置位置,初始值为-1,程序中略去了初始化
static int sum = 0;//记录解的个数
bool canPlace(int k, int i)
/*
*用来判定第k个皇后能不能放置在第k行的i位置上。
*因为每行只能放置一个皇后是肯定的事情,所以8个皇后必然是每行放置一个,而我们放置皇后的时候是第i个放置在第k行的。这样给出两个参数即皇后编号k和欲放置位置即可
*k既代码行号,又代表皇后的编号
*/
{
for( int j = 0; j < k; j++)
/*
*这个循环的范围是[0,k)而不是[0,QUEUE)
*因为对于canPlace函数来说,它的任务是判定第k个皇后能不能放置在第k行的第i列即可以类,此时[k+1,QUUE)都还没有放置皇后
*因此没有必要对其进行判定。
*很显然对于这样的判定任务使用循环是正常的策略
*/
{
if( abs(queues[j]-i) == abs(k-j) || queues[j] == i)
/*
*abs(queues[j]-i) == abs(k-j)是判定第k个皇后放置在第k行的第j列时,其对角线上有没有已经放置皇后。
*queues[j] == i是判定第k个皇后放置在第k行的第j列时,该行有没有放置皇后。
*/
return false;
}
return true;
}
void printSolution()
{
cout << sum << " solution is: " << endl;
for( int i = 0; i < QUEUE; i++)
cout << "queues[" << i << "] = " << queues[i] << endl;
}
void queueSolution(int k)
{
/*
*这里传入了一个参数,前面有说k的含义,既代表行号,又代表皇后的编号。
*使用该函数时,传入参数k=0,表示第一个皇后放置在第一行
*这是我们开始解八皇后问题的起点,即在第一行试着放置第一个皇后。
*这个函数的任务是一个放置好一个皇后,然后递归
*/
for( int j = 0; j < QUEUE; j++)
/*
*这个循环是“体现和实现回朔的关键”。用来判定欲放置在第行的第k个皇后能不能放置在第k行的第j列。
*传入参数是k,k既代表行号,又代表皇后的编号。就表示尝试第k行能不能放置第k个皇后,如果成功放置,再去放置第k+1个皇后
*而这个循环则是对列的遍历,即尝试应该放置在第k行的第k个皇后能不能放置在这行的第i列上。通过这个循环就可以得到所有的i值
*而当第k行的第k个皇后顺利(暂时顺利)放置在第i列(第一个i值)时,else queueSolution(k+1);代码引发递归调用,此时
*for循环暂停,转而程序跳转到递归的queueSolution函数又进行这个过程。
*现在假设这个过程执行完毕,那么应该回到这个循环继续执行,即继续寻找适合的i值。当嵌套调用顺利的结束递归时,那么就表示已经找到了一个解决方案
*此时for循环的继续运行是继续寻找另一个解决方案;当嵌套调用没有顺利结束递归时,那么就表示这个暂时顺利的i值是无效的,此时这个for循环的继续运行
*是继续寻找正确的解决方案。这里我用了“继续”两个字描述为第k行的第k个皇后寻找适合的i位置的过程,这“回朔”的体现:嵌套以暂停循环进而深入执行,然后又返回朔到循环继续执行
*/
{
if (true == canPlace(k,j))
{
queues[k] = j;
if(k == QUEUE-1)//皇后全部放置好了,此时找到了一个解决方法
{
sum++;
printSolution();
cout << endl;
}
else queueSolution(k+1);//递归,不管递归是否成功,都会回朔到for循环继续执行,所以这个函数可以将所有的解方案找出来
}
}
}
int main(int argc, char ** arg)
{
queueSolution(0);
return 0;
}
另外,8皇后的解有92种。