8皇后问题求解
2017-05-09 00:09
120 查看
1. 八皇后问题的递归解法。
题目一看就是递归,因为问题描述可以表示为在第k层放好的情况下,放第k+1个皇后,使与前面的不相冲突。
好,现在分析第k层算法(递归算法)
若k层==9, 表示搜索已经结束,打印结果,返回。
否则,在8个列上试探,若本列无皇后,正对角线无皇后,斜对角线无皇后,在该处放置皇后。
递归到下一层,即下一行(在下一行放皇后)。
问题虽然描述清了,但写代码还为时过早,还要明确几个问题。
1. 何为搜索?
计算机搜索就是计算机枚举所有可能的情况, 就是所谓的穷举.搜索就是建立结果数组的过程,通过调试你会发现,搜索大部分的时间是在进行比较,运算, 很少部分时间是构建结果数组.
我们把这种构建数组的尝试,递进的过程叫搜索算法.
2. 要打印结果,那结果是怎样表示的 ?
结果是一个两维数组a[i][j],代表棋盘,当放上皇后用1表示,不放皇后用0表示,很形象,对吗。打印的时候,你甚至可以打出图形来。
3. 怎样判断a[i][j]这个点能否放皇后?
四个条件,本行,本列,对角线和反对角线都不能有皇后可以简化为3个条件,把本行去掉,因为每行只放一个皇后。放好后,我们就递归到下一行.
或者这一行所有位置都不能放皇后,我们就回溯。
其中i就是行,先放a[0][j],再放a[1][j]…最后a[8][j], 层数到9就可以打印前面结果了
4. 什么叫放置一个皇后?
所谓放置皇后,就是把a[i][j]数组中一个元素赋值为1.同时把本列置为有皇后,把对应的正对角线和反对角线置为有皇后
每一列有没有皇后用一个数组a1[8]来表示, a1 中对应值为1,表示该列有皇后,为0表示无皇后.
同理,正对角线15个用a2[15]来表示, 反对角线15条用a3[15]来表示.
例如: 我要在a[3][1]上放皇后, 那要看a1[1]上是否有皇后, 还要看a2[4]上是否有皇后,还要看a3[9]上是否有皇后.
若都满足条件,则:
a[3][1] = 1; a1[1] = 1; a2[4] = 1; a3[9] = 1;
5. 列数组,对角线数组,反对角线数组
列数组 a1[8], j 列值就是它的下标对角线数组 a2[15]
考察普通笛卡尔坐标下的棋盘。(0,0)第一条线 (1,0)(0,1)第二条线 (2,0)(1,1)(0,2)第三条线...(7,0)..(0,7)第8条线 row+column 是常数,继续(7,1)..(1,7)为第9条线,第(7,7)为第15条线。 所以,当a[i][j]置1时, 将a2[i+j] 置1表示占据了该条对角线.
反对角线数组 a3[15]
看(0,0)(1,1)。。(7,7)构成一条线。这是中间。row=column, row-column=0 向两边扩散也有2n-1条对角线。右上(0,7)第一条,然后(0,6)(1,7)第二条... (0,1)(1,1)第8条. 继续数到第15条. 反对角线的下标index可以用 7+row-column 表示.
所以,当a[i][j]置1时, 把a3[7+i-j] 也置1, 表示占据了该条反对角线
通过以上分析,我们知道当放置一个皇后时a[i][j]=1, 把对应的3个辅助数组也置1表示占用
递归算法和逻辑关系.
递归算法就是queen(0)->queen(1)->queen(2)->…queen(8) 然后打印结果
在每一层,都要逐列试探是否可以放置皇后, 可放,放置后继续向下递归,不可放,
试探下一列,8列均已试满,回退!
补充,逐行放置与逐列放置是等价的. 而且由4角对称性可知,结果应该是4的倍数.
实测结果是92, 符合这个判断!
据以上分析,整理数据结构和逻辑关系,可以写出c代码程序如下:
递归代码实例
/* author: hjjdebug * date : 2009 * 再整理 : 2017 */ #include <stdio.h> //建立笛卡儿坐标系,i 为y轴,方向向下, j为x轴,方向向右. 左上角为原点 int a[8][8]; int a1[8]; //列值辅助数组,a1[j], 范围, a1[0]-z1[7] , 8个元素 int a2[15]; //正对角线辅助数组a2[i+j], 范围 a2[0]-a2[14] , 15个元素 int a3[15]; //反对角线辅助数组a3[i-j+7], 范围 a3[0]-a3[14] , 15个元素 void print_result() { for(int i=0; i<8; i++) { for(int j=0; j<8; j++) { printf("%c ",a[i][j]==1 ? '*' : '-'); } printf("\n"); } } static int count=0; void queen( int i) { int j; if(i==8) { printf("count:%d\n", ++count); print_result(); return; } for(j=0; j<8; j++) { //判定a[i][j] 能否放皇后 if(a1[j]==0 && a2[i+j]==0 && a3[i-j+7]==0) { a[i][j] = 1; a1[j]=1; a2[i+j]=1; a3[i-j+7]=1; queen(i+1); //恢复环境, 是回退的意思,以便查找下一位置. a[i][j]=0; a1[j]=0; a2[i+j]=0; a3[i-j+7]=0; } } } int main() { queen(0); }
代码很简洁,嗯! 是的.
递归算法是自己调用自己的算法,实际上属于深度优先搜索算法. 每调用一次自己,就向更深一层前进了一步.
递归返回时,会逐层返回,或叫逐层回退. 返回并不是一退到底,而是只退一层, 根据具体情况,可以选择其它操作,
是继续进,还是继续退.
递归利用函数栈存储数据和利用函数返回实现自动回退。 这实际上是隐含回溯。
2.八皇后问题的非递归解法。 回溯法
这里给一个不用递归用循环的程序,看其怎样实现回溯.虽然非递归不如递归简洁。 但其进退之间别有一番情趣! 而且它还可以实现2级(本题)或多级深度退却(例如迷宫).
本题循环的退出条件是column 被逼回为负数, 搜到结果是colum=8 有点新意!
1.该程序的结果是如何表示的?
用1个一维数组a[]来表示, a[0]表示第一列皇后所在的行数, a[1]表示第二列皇后所在的行数… a[7]表示第8列…2.该程序的算法过程是怎样的?
先填充第一列皇后a[0],填好后, 向前进一步,填充第二列. 当8列都填好后,就可以打印结果了.如果当向下列前进时, 所有位置(所有行)都不能满足要求,则要回退到本列, 以便搜索其它值, 若本列已没有
值可以搜索,则继续回退.
3.前进和回退是什么意思?
前进就是列column加1,然后继续操作,回退就是列(column)减1,并恢复现场, 再搜索其它值.4.搜索到结果和搜索结束条件是什么?
当列递进到8时,表示a[0]-a[7]都填好了,可以打印结果了.当列回退到-1时,表示全部搜索完成.
简单分析: 结果保存在 row[]中, row[0]保存第一列的行值, row[1]保存第二列的行值…
5.非递归实例代码
/* author: hjjdebug * date : 2009 * 整理 : 2017 */ #include <stdio.h> #include <stdlib.h> #define MAXCOLUMN 8 #define REALMAXCOLUMN (MAXCOLUMN - 1) int row[MAXCOLUMN]; //保存每一列对应的行值 int c; //column int a1[8]; //表示莫一行是否有皇后, 0->有皇后,1->无皇后, 下标表示行值 //正斜线和反斜线都是从上往下顺序数, 从0开始 int a2[2*MAXCOLUMN-1]; // 表示对角线皇后配置, 下标表示第几条斜线 int a3[2*MAXCOLUMN-1]; // 表示反对角线皇后配置, 下标表示第几条反斜线 void init(); void printResult(); void backward(); int main() { init(); // c: the column, from 0 to REALMAXCOLUMN // 当c等于 REALMAXCOLUMN 时,表示找到了一个结果 // 当c被逼回到负数时,表示完全搜索结束。 while (c>=0) { if(a1[row[c]]&&a2[c+row[c]]&&a3[REALMAXCOLUMN-c+row[c]]) { if (c==REALMAXCOLUMN) { printResult(); backward(); } else { /*在第c列,row[c]行位置设定有皇后标志*/ a1[row[c]]=a2[c+row[c]]=a3[REALMAXCOLUMN-c+row[c]]=0; //列加1,从第1行开始配置 row[++c]=0; } } else { backward(); } } return 0; } //变量说明: c是列值,row[]是行值数组,也是结果数组 //a1[]是行数组,a2[]是对角线数组,a3[]是反对角线数组.值为1表示未被占用 void init() { int i; for (i=0;i<MAXCOLUMN;i++) a1[i]=1; for (i=0;i<2*MAXCOLUMN-1;i++) a2[i]=a3[i]=1; for (i=0;i<MAXCOLUMN;i++) row[i]=0; c=0; } //打印结果,让行号从1开始 void printResult() { for (int i=0;i<MAXCOLUMN;i++) { printf("%d ",row[i]+1); } printf("\n"); } void backward() { while (row[c]==REALMAXCOLUMN) { c--; //列减1 //清除关于第c列,row[c]行有皇后标志 a1[row[c]]=a2[c+row[c]]=a3[REALMAXCOLUMN-c+row[c]]=1; } row[c]++; //行加1 }
相关文章推荐
- C语言编程 N皇后问题求解
- 关于n皇后问题的递归求解,有详细的步骤说明。
- 深度优先—递归方法 求解n皇后问题
- 回溯法解决N皇后问题——递归与非递归求解
- N皇后问题回溯法求解
- 对求解“皇后排列问题”的一个新解法!
- N皇后问题的位运算求解——目前最快的方法
- 数据结构与算法中的“递归”——用回溯法求解8皇后问题
- 八皇后问题求解1
- 求解高斯8皇后问题
- 经典算法(1)——8皇后问题求解(回溯法)
- 递归求解8皇后问题
- 8皇后问题递归求解
- 非递归回溯算法求解n-皇后问题
- 回溯法求解 “n 皇后 问题”——Java 实现
- 经典8皇后问题求解,C#2.0版本
- 递归求解N皇后问题(c语言)
- N皇后问题的求解(C++代码)
- QS2算法求解N-皇后问题
- 递归与迭代法求解N皇后问题