八皇后问题描述

问题: 国际象棋棋盘是8 * 8的方格,每个方格里放一个棋子。皇后这种棋子可以攻击同一行或者同一列或者斜线(左上左下右上右下四个方向)上的棋子。在一个棋盘上如果要放八个皇后,使得她们互相之间不能攻击(即任意两两之间都不同行不同列不同斜线),求出一种(进一步的,所有)布局方式。

首先,我们想到递归和非递归两类算法来解决这个问题。首先说说递归地算法。

很自然的,我们可以基于行来做判断标准。八个皇后都不同行这是肯定的,也就说每行有且仅有一个皇后,问题就在于皇后要放在哪个列。当然八个列下标也都不能有相同,除此之外还要保证斜线上不能有重叠的皇后。

第一个需要解决的小问题就是,如何用数学的语言来表述斜线上重叠的皇后。其实我们可以看到,对于位于(i,j)位置的皇后而言,其四个方向斜线上的格子下标分别是 (i-n,j+n), (i-n,j-n), (i+n,j-n), (i+n,j+n)。当然i和j的±n都要在[0,7]的范围内,保持不越界。暂时抛开越界限制不管,这个关系其实就是: 目标格子(a,b)和本格子(i,j)在同一条斜线上 等价于 |a - i| == |b - j| 。

然后,从递归的思想来看,我们在从第一行开始给每一行的皇后确定一个位置。每来到新的一行时,对本行的所有可能位置(皇后放在这个位置和前面所有已放置的皇后无冲突)分别进行递归地深入;若某一行可能的位置数为0,则表明这是一条死路,返回上一层递归寻找其他办法;若来到的这一行是第九行(不存在第九行,只不过是说明前八行都已经正确配置,已经得到一个解决方案),这说明得到解决方案。

可以看到,寻找一行内皇后应该摆放的位置这是个递归过程,并且在进入递归时,应该要告诉这个过程的东西包括两个: 1. 之前皇后放置的状态, 2. 现在是第几行。

所以,递归主体函数可以设计为 EightQueen(board, row),其中board表示的是当前棋盘的状态(比如一个二维数组,0表示未放置,1表示放有皇后的状态)。另外还可以有一个check(board,pos),pos可以是一个(x,y)元组,check函数用来返回以当前的board棋盘状态,如果在pos再放置一个皇后是否会有冲突。

基于上面的想法,初步实现如下:

defcheck(board,pos):#check函数暂时先不实现

pass

defEightQueen(board,row):

blen=len(board)if row == blen: #来到不存在的第九行了

printboardreturn True #一定要return一个True,理由在下面

for possibleY inrange(blen):ifcheck(board,(row,possibleY)):

board[row][possibleY]= 1 #放置一个Queen

if not EightQueen(board,row+1): #这里其实是本行下面所有行放置皇后的递归入口。但是如果最终这条路没有找到一个解,那么

#此时应该将刚才放置的皇后收回,再去寻找下一个可能的解

board[row][possibleY] =0else:returnTruereturn False

最开始,可能在回归返回条件那里面不会想到要return True,而只是return。对应的,下面主循环中放置完Queen之后也只是简单地递归调用EightQueen,不会做逻辑判断。但是很快可以发现这样做存在一个问题,即当某一层递归中for possibleY这个循环走完却没有找到一个合适的解(即本行无合适位置),此时返回上一行,上一行的possibleY右移一格,此时之前放在这一行的Queen的位置仍然是1。这样之后本行的所有check肯定都是通不过的。所以我们需要设计一个机制,使得第一个possibleY没有找到合理的最终解决方案(这里就加上了一个判断条件),要右移一格到下一个possibleY时将本格的Queen收回。

这个判断条件就是如果某层递归for possibleY循环整个走完未找到结果返回False(EightQueen整个函数最后的返回),上一层根据这个False反馈把前一个Queen拿掉;如果找到了某个结果那么就可以一路return True回来,结束函数的运行。

另外,如果只是获取一个解的话,可以考虑在if row == blen的时候,打印出board,然后直接sys.exit(0)。此时就只需要for possibleY循环完了之后return一个False就可以了。当然主循环中对于递归的返回的判断 if not EightQueen还是需要的。

上面没有实现check函数。其实仔细想一下,如果按照上面的设想来实现check函数还是有点困难的。比如令 x,y = pos,尽管此时我们只需要去检查那些行下标小于x的board中的行,但是对于每一行中我们还是要一个个去遍历,找到相关行中值是1的那个格子(突然发现这个是one-hot模式诶哈哈),然后将它再和x,y这个位置做冲突判断。所以但是这个check函数复杂度就可能会达到O(n^2),再套上外面的循环,复杂度蹭蹭往上涨。下面是check函数的一个可能的实现:

defcheck(board,pos):

x,y=pos

blen=len(board)for i inrange(x):for j inrange(blen):if board[i][j] == 1:if j == y or abs(j-y) == abs(i-x):returnFalsereturn True

其实可以看到,我们花了一层循环在寻找某行中的one-hot,那些大量的0值元素是我们根本不关心的。换句话说,对于board这个二维数组,其实我们真正关心的是每行中one-hot值的下标值。自然我们就可以想到,能不能将board转化为一个一维数组,下标本身就代表了board中的某一行,然后值是指这一行中皇后放在第几列。

如果是这样的话,那么程序就需要改造,首先是check函数要根据新的board数据结构做一些调整:

defcheck(board,row,col):

i=0while i

i+= 1

return True

可以看到,改变二维数组board变为一维数组之后,我们可以在O(1)的时间就确定row行之前每一行摆放的位置,并将其作为参考进行每一行的冲突判断。

然后是主函数的修改:

defEightQueen(board,row):

blen=len(board)if row == blen: #来到不存在的第九行了

printboardreturnTrue

col=0while col

board[row]=colif EightQueen(board,row+1):returnTrue

col+= 1

returnFalsedefprintBoard(board):'''为了更友好地展示结果 方便观察'''

importsysfor i,col inenumerate(board):

sys.stdout.write('□' * col + '■' + '□' * (len(board) - 1 -col))print ''

总的结构,和没修改之前是类似的,只不过在主循环中,从上面的possibleY作为游标去设置 - 去除 一个位置的放置状态,这种方式改为了简单的col += 1。改成col+=1的好处就是当某轮递归以失败告终,返回上层递归之后,就不用再去特地收回之前放置好的Queen,而是可以直接让col += 1,。

printBoard函数可以将一维数组的board状态很直观地展现出来:

■ □ □ □ □ □ □ □

□ □ □ □ ■ □ □ □

□ □ □ □ □ □ □ ■

□ □ □ □ □ ■ □ □

□ □ ■ □ □ □ □ □

□ □ □ □ □ □ ■ □

□ ■ □ □ □ □ □ □

□ □ □ ■ □ □ □ □

所有结果

上面的程序多只是生成了一个结果,而实际上八皇后可以有很多种可能的布局。如何才能求得所有结果?其实只要小小地修改一下上面的程序就可以了。

以上面修改过后一维数组维护棋盘状态为例。程序在碰到一次row == blen的情况之后就返回了True,然后递归一层层地返回True直到最上层。所以找到一个解决方案之后,程序就会退出了。

反过来,如果获得一个解决方案之后,不判断EightQueen函数的返回,此时函数会继续执行col += 1,将状态搜寻继续下去,如此收集状态的任务在row == blen的判断中,(注意这里的return可不能删,这里需要一个return来提示递归的终结条件),而对于每条递归路径总是穷尽所有可能再回头,这样就可以获得到所有可能了:

defEightQueen(board,row):

blen=len(board)if row == blen: #来到不存在的第九行了

printboardreturnTrue

col=0while col

board[row]=colif EightQueen(board,row+1):#return True 去掉这里即可,或者直接删除掉整个判断,只留下单一个EightQueen(board,row+1)

passcol+= 1

return False

示例结果:

[0, 4, 7, 5, 2, 6, 1, 3]

[0,5, 7, 2, 6, 3, 1, 4]

[0,6, 3, 5, 7, 1, 4, 2]

[0,6, 4, 7, 1, 3, 5, 2]

[1, 3, 5, 7, 2, 0, 6, 4]

[1, 4, 6, 0, 2, 7, 5, 3]

[1, 4, 6, 3, 0, 7, 5, 2]

[1, 5, 0, 6, 3, 7, 2, 4]

[1, 5, 7, 2, 0, 3, 6, 4]

…… 总共有92种布局方案

非递归

非递归解这个问题,很显然是要去维护一个stack来保存一个路径了。简单来说,这个栈中维护的应该是“尚未尝试去探索的可能”,当我开始检查一个特定的位置,如果检查通过,那么应该做的是首先将本位置右边一格加入栈,然后再把下一行的第一个格子加入栈。注意前半个操作很容易被忽视,但是如果不将本位置右边一格入栈,那么如果基于本格有皇后的情况进行的递归最终没有返回一个结果的话,接下来就不知道往哪走了。如果使用了栈,那么用于扫描棋盘的游标就不用自己在循环里+=1了,循环中游标的移动全权交给栈去维护。

代码如下:

defEightQueen(board):

blen=len(board)

stack=Queue.LifoQueue()

stack.put((0,0))#为了自洽的初始化

while notstack.empty():

i,j=stack.get()if check(board,i,j): #当检查通过

board[i] = j #别忘了放Queen

if i >= blen - 1:print board #i到达最后一行表明已经有了结果

break

else:if j < blen - 1: #虽然说要把本位置右边一格入栈,但是如果本位置已经是行末尾,那就没必要了

stack.put((i,j+1))

stack.put((i+1,0)) #下一行第一个位置入栈,准备开始下一行的扫描

elif j < blen - 1:

stack.put((i,j+1)) #对于未通过检验的情况,自然右移一格即可

显然,把break去掉就是求所有解了

C语言版

#include

static int board[8] ={};int board_size = sizeof(board)/sizeof(int);int check(int *board,introw){int i = 0;while(i

}

i++;

}//printf("board[%d]: %d\n",row,board[row]);

return 1;

}void print_board(int *board){inti;int size =board_size;for(i=0;i

printf("%d,",board[i]);

}

printf("\n");

i= 0;while (i

printf("%s","■");

}else{

printf("%s","□");

}

}

printf("\n");

i++;

}

}int eight_queen(int *board,introw){if (row == 8){

print_board(board);return 1;

}

board[row]= 0;while (1){if (check(board,row) && eight_queen(board,row+1)){return 1;

}else{if(++board[row] >= 8){break;

}

}

}return 0;

}intmain(){

eight_queen(board,0);//print_board(board);

return 0;

}

八皇后问题python_八皇后问题Python实现相关推荐

  1. 八年级信息技术python_八年级信息技术上册第2课走进Python课件南方版

    版权声明:以上文章中所选用的图片及文字来源于网络以及用户投稿,由于未联系到知识产权人或未发现有关知识产权的登记,如有知识产权人并不愿意我们使用,如果有侵权请立即联系:55525090@qq.com,我 ...

  2. 八皇后问题python_python八皇后问题

    两种方法 第二种方法解释 第一层for表示第一个皇后的位置,然后第二层for表示,循环8次,表示其他皇后的位置,最后限制次数,是if判断 def conflict(state,nextx): '定义冲 ...

  3. 八皇后问题和八数码问题的最陡上升爬山法、首选爬山法、随机重启爬山法、模拟退火算法的分析和实现

    对经典算法的问题的回顾与感想 对八皇后问题和八数码问题分别用最陡上升爬山法.首选爬山法.随机重启爬山法.模拟退火算法来实现,并且分析他们的性能. 分析 要求实现的各个算法是有共同点的,比如,八皇后问题 ...

  4. 【恋上数据结构】回溯、剪枝(八皇后、n皇后)、LeetCode51.N皇后、LeetCode52.N皇后 II

    回溯 回溯(Back Tracking) 提出八皇后问题(Eight Queens) 初步思路一:暴力出奇迹 初步思路二:根据题意减少暴力程度 初步思路三:回溯法(回溯+剪枝) 四皇后 - 回溯法图示 ...

  5. 爬山法、随机重启爬山法、模拟退火算法对八皇后问题和八数码问题的性能测试...

    代码地址:https://github.com/laiy/AI/tree/master/awesome-search 一些前提: 1. 首先要明确这些算法并不是用于解决传统的搜索问题的(环境是可观察的 ...

  6. java 八皇后问题以及N皇后问题

    想了解更多数据结构以及算法题,可以关注微信公众号"数据结构和算法",每天一题为你精彩解答.也可以扫描下面的二维码关注 八皇后的来源 八皇后问题是一个以国际象棋为背景的问题:如何能够 ...

  7. 八皇后加强版:每个皇后最多攻击一个其它的皇后

    想必搞OI/ACM的朋友都应该知道八皇后问题,这是学习编程的必修课程之一:在国际象棋棋盘上最多可以放置多少个互不攻击的皇后(皇后可以攻击它所在的行.列.对角线方向上的棋子)?显然,能够放置的皇后数不超 ...

  8. 2021-01-11经典的八皇后问题和N皇后问题, 回溯

    八皇后的来源 八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或斜线 ...

  9. 四核八线程和八核八线程区别

    四核八线程和八核八线程来比差距有多少? 1.四核八线程是指使用了超线程技术 , 把一个物理核心,模拟成 两个逻辑核心, 理论上要像八颗物理核心一样在同一时间执行八个线程,所以设备管理器和任务管理器中会 ...

最新文章

  1. JS事件调试 - 查找HTML元素绑定的事件以及绑定代码所在位置
  2. mysql 时间取日期函数_mysql 获取当前日期函数及时间格式化参数详解
  3. PL/SQL在win7/win8 x64位下使用客户端连接oracle
  4. CSS中display:block、inline、inline-block
  5. k8s核心技术-Helm(概述)---K8S_Google工作笔记0044
  6. oracle out参数查询,Oracle的out参数实例详解
  7. try catch无法捕获 StackOverflowException
  8. matlab dpabi安装,Android 8 应用安装时 ABI 确认过程
  9. 加载字典文件,扫描网站潜在目录
  10. 小米球ngrok如何后台启动
  11. Marlin关于如何接收Gcode指令的详解
  12. Greenplum 6 磁盘配额管理工具“Diskquota”
  13. 冯仑《企业领导最容易犯的十大错误》
  14. 《JavaScript》条件运算符
  15. PL3369C原边12W电源芯片
  16. Centos实现软路由
  17. 【ReID】Pyramidal Person Re-IDentification via Multi-Loss Dynamic Training
  18. Java基础--数值和字符串
  19. http://www.jb51.net/article/84149.htm
  20. efi分区格式化操作

热门文章

  1. 28 岁自学 java,包装简历 3 年拿到 15k 薪资,分享我的学习经历
  2. 成就小我,从第一篇博客开始
  3. seo 做搜索引擎优化优化置信每小我都对外链有很深的看法
  4. Calabash Android安装
  5. 待业将近两个月,晚上11点接到面试邀约电话,我却拒绝了...
  6. 关于生物发酵工程展(简称:BIO CHINA 2020)举办通知
  7. keil5左边的project窗口打开或者放置原位
  8. echarts中饼图的指示线前加圆点
  9. G2O 图优化基础与示例汇总
  10. 三星与苹果的平板电脑的市场份额共同增长