所有源码都在github上(https://github.com/seasonyao/eight_queen_question)
如果你去百度百科八皇后这个问题,你会发现人家也是历史上有头有脸的一个问题,最后一句“计算机发明后就有一万种方式解决这个问题”读起来也让程序猿们很快活。闲话少说,开始阐述我的思路:
最无脑的解法一定是八个for遍历,浪费了太多的计算资源在各种无用功上面,我们稍微构思一下:
首先如何决定下一个皇后能不能放这里可以有两种思路,第一种是尝试维护一个8*8的二维矩阵,每次找到一个空位放下一个皇后就把对应行列对角线上的棋格做个标记,如果某行找不到可放皇后的格子就把上一个皇后拿走并把对应行列对角线的标记取消掉;第二种方法直接放弃构造矩阵来假装棋盘,我们把问题更加抽象化,八个皇后能放下一定是一行放一个,我们只需一个数组记录每个皇后的列数(默认第N个放第N行),那么问题就被抽象成了数组的第N个数和前N-1个数不存在几个和差关系即可(比如差不为零代表不在同一列)。
接着想想问题中存在着大量的循环怎么解决比较高效,我们知道递归和迭代一定程度上是可以很容易做到互相转化实现同样的思路的。递归是重复调用函数自身实现循环,迭代是函数内某段代码实现循环,使用递归的话我们应该要有一个能在第N行找到某一列的格子可以放皇后的函数,能找到把参数+1去调用自己去找下一行皇后能放的格子,找不到就算了。如果想用迭代,前面我们说过递归迭代是可以转化的,这种在函数最后调用自己的递归更是极易转化,我们按着迭代的套路在for循环的里按照刚刚递归的思路加几个判断判别循环是continue、break还是返回前一层循环即可。最后还有一种思路,准确来说还是和递归脱离不了关系,学习递归的时候我们我们知道,递归可以看做底层帮你维护的一个堆栈不断地push、pop,知道它的本质我们也可以通过手动维护一个堆栈来模拟这个递归调用的过程,只要构造两个函数backward(往后回溯)、refresh(向前刷新)来模拟堆栈进出即可。
最后我们来分析四个方法(矩阵维护法、递归法、迭代法、手动堆栈法)表现和改进,很明显在代码量上递归会是最短的,而需要运行的空间来看手动堆栈也会比较必要更大的运行内存(如果用VS运行手动堆栈的代码,很有可能会提示你stack溢出,那么你需要修改一下VS的配置给你的程序分配更大的内存)。八皇后问题有很多小细节可以改进(具体实现大家自己来,为了方便我就说一些我想到的点):很明显棋盘是对称的,如果你得出了一个解法那么一定有行对称列对称对角线对称的另外三种对称的摆法,这样就可以减少一些计算量。
头脑风暴过后,结合代码和注释讲解具体实现过程:
1.矩阵维护法
这是第一个出现在我头脑中的方法,很桑心居然不是递归,看来脑子还不够抽象。上代码:
//八皇后维护矩阵法
#include<iostream>
using namespace std;
int cheese_table[8][8];
int queen[8];//记录五个皇后的列数
int lastqueen=-1;
int solution=0;
int search_line(int i,int j){//搜寻这一行有没可放的位置for(;j<8;j++)if(cheese_table[i][j]==0)return j;return -1;
}
void set_queen(int i,int j){//在可放的位置上放上皇后记录下来并对棋盘进行操作cheese_table[i][j]=-1;queen[i]=j;for(int temp=0;temp<8;temp++)//列操作if(cheese_table[temp][j]!=-1)