详细注解八皇后问题

来源:互联网 发布:土巴兔云设计软件 编辑:程序博客网 时间:2024/05/22 03:09

网上八皇后问题的代码一大把,可是“回朔递归”还是理解的不好!

这里是我一步步写出的代码和详细的注释,解释了这个问题的解决用到的“回朔和递归”体现在哪里和怎么体现的两个问题,相信看了之后,对这个问题的解决原理会有深刻的理解的。


如下:

#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值)时,elsequeueSolution(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;}elsequeueSolution(k+1);//递归,不管递归是否成功,都会回朔到for循环继续执行,所以这个函数可以将所有的解方案找出来}}}int main(int argc, char ** arg){queueSolution(0);return 0;}

另外,8皇后的解有92种。