N 皇后是回溯算法经典问题之一。问题如下:请在一个 ni n 的正方形盘面上布置 n 名皇后,因为每一名皇后都可以自上下左右斜方向攻击,所以需保证每一行、每一列和每一条斜线上都只有一名皇后。

最简单的办法是暴力法,我们需要在 n2 个空格里选 n 个位置,所以可以依次 Cnn2 尝试种选择。暴力法的时间复杂度为 O(nn)。如果用回溯算法,时间复杂度降低为 O(n!)。因为 n 的大小对算法思路没有任何影响,所以简单起见,示例将解决 4 皇后问题。

N 皇后问题的算法与数独十分相似,同样是试错的思想。在解题过程中,我们动态地修改一个题解,让这个题解永远保持成立性。在解决 4 皇后问题中,我们一共要放置 4 名皇后,并且保证每行每列和每条斜线上只有一名皇后。我们不断地检查,如果题解不再满足约束条件,就改变它。

1) 如图 1 所示,用 1 表示皇后,我们从第一个空格开始,把皇后放在格中。

图 1:4 皇后(1)

只要盘面还有继续布置皇后的可能,就继续下去。当碰到盘面不成立或者没有布置可能的时候,再后退。我们知道第一行不能有两名皇后,所以从第二行继续。

2) 如图 2 所示,假设第二名皇后在第二行第一个空格。

图 2:4 皇后(2)

在继续到第三行之前,先检查一下盘面。如图  2 所示,因为盘面不成立,我们只好改变第二名皇后的位置。

3) 第二行的第二个空格不可行因为两名皇后将占用同一斜线。所以我们将第二名皇后放在第二行的第三个空格上,这时盘面是成立的,我们前进一行,如图 3 所示。

图 3:4 皇后(3)

我们发现第三行皇后没有可行的位置。这时候,我们碰到了“南墙”,所以回溯算法告诉我们:后退一行。

4) 如图 4 所示,我们退回到第二行,将第二名皇后放在下一个空格上。因为盘面成立,所以我们再次前进到第三行。

图 4:4 皇后(4)

5) 如图 5 所示,经过尝试,我们把第三名皇后放在第二个空格里。然而,来到第四行时,我们发现无可行选项。我们只好再次返回到第三行,却发现第三行也无其他可行选项。我们除了后退到第二行没有其他选择,但再次面临同样的问题,第二名皇后已经在最后一个空格了,没有其他选项了。

图 5:4 皇后(5)

6) 如图 6 所示,我们只好后退到第一行,将第一名皇后换到下一个空格中。

图 6:4 皇后(6)

7) 如图 7 所示,重复以上步骤我们最终会得到正确答案之一。我们将会把答案记录下来,并继续没完成的过程,从而得到所有的答案。在本例中,我们以行为单位。如果以列为单位同样可行。

图 7:4 皇后(7)

在 N 皇后问题中,回溯算法思路是每一次只布置一个皇后,如果盘面可行,就继续布置下一个皇后。一旦盘面陷入死局,就返回一步,调整上一个皇后的位置。重复以上步骤,如果解存在,我们一定能够找到它。

下面我们来看如何将以上步骤转换为代码。可以看到,我们在重复“前进—后退—前进—后退”这一过程。问题是,我们不知道一共需要重复这个过程多少次,也不能提前知道 n 是多少,更不知道每一次后退时需要后退几行,因此我们不能利用 for 循环和 while 循环来实现这个算法。

因此我们需要利用递归来实现代码结构。逻辑如下:当方法布置完当前行的皇后,就让方法调用自己去布置下一行的皇后。当盘面变成绝境的时候,就从当前方法跳出来,返回到上一行,换掉上一行的皇后再继续。

我们定义 NQueens(n) 方法,它负责输出所有成立的 ni n 盘面。其中 1 代表皇后,0 代表空格。

例如,NQueens(4) 输出:

[

[ [0,1,0,0],

[0,0,0,1],

[1,0,0,0],

[0,0,1,0]

],

[ [0,0,1,0],

[1,0,0,0],

[0,0,0,1],

[0,1,0,0]]

]

在 NQueens() 方法中,我们会定义 helper(x) 方法帮助实现递归结构。helper(x) 方法负责布置第 x 行到最后一行的皇后,它在布置完当前行的皇后之后会调用自己,接着布置剩余行的皇后。递归的边界条件是当 x 等于 n 时,也就是盘面已经完整的时候,helper() 方法会把当前的盘面加进结果列表。

在解题过程中,我们需要一个变量用于储存当前盘面解,我们会动态地修改这个变量直到盘面满足条件。我们有两种储存方式,第一个是声明一个 ni n 的二维数组,初始是全部为 0,在解题过程中放置 1。比如:

[

[0,1,0,0],

[1,0,0,0],

[0,0,1,0],

[0,0,0,1]

]

代表第一个皇后在第二个格子,第二个皇后在第一个格子,第三个皇后在第三个格子,第四个皇后在第四个格子。

第二种储存方式是利用一个长度为 n 的一维数组,其中第 i 个数值代表第 i 行皇后的坐标。比如 [1,0,2,3] 所表示的与上面的二维数组一致。因为两种方式储存的信息量一样,但是第二个选项占用更少的空间,所以我们优先选择第二个选项。

以下是完整代码:

#输出所有成立的n·n盘面

def NQueens(n):

#检查盘面是否成立

def checkBoard(rowIndex):

for i in range(rowIndex):    #rowIndex是当前行数

if cols[i]==cols[rowIndex]: #检查竖线

return False

if abs(cols[i]-cols[rowIndex]) == rowIndex-i:#检查斜线

return False

return True

#布置第rowIndex行到最后一行的皇后

def helper(rowIndex):

if rowIndex==n:         #边界条件

board = [[0 for _ in range(n)] for _ in range(n)]

for i in range(n):

board[i][cols[i]] = 1

res.append(board)      #把当前盘面加入结果列表

return            #返回

for i in range(n):       #依次尝试当前行的空格

cols[rowIndex] = i

if checkBoard(rowIndex):   #检查当前盘面

helper(rowIndex+1)    #进入下一行

cols = [0 for _ in range(n)]    #每一行皇后的纵坐标

res = []              #结果列表

helper(0)              #从第1行开始

return res

在 NQueens() 方法中我们定义了两个子方法:checkBoard() 和 helper()。我们直接调用 helper(0),让 helper() 方法帮我们解决问题。最后直接输出 res 答案集合。

首先注意要在调用 checkBoard() 和 helper() 前定义它们,否则程序会报错。另外一种写法是将这两个方法放在 NQueens() 外面。但是在方法中定义子方法的好处是我们可以直接调用 cols,n,res这些存在于 NQueens() 方法范围内的变量。如果把子方法定义在 NQueens() 外面,这些变量必须作为参数传入子方法,写起来相对比较烦琐。

我们声明以下两个变量的用途如下:

cols:这是我们当前的题解,第 i 个数值代表第i行皇后的坐标。比如 [1,0,2,4] 代表第一行的皇后在第二个空格,第二行的皇后在第一个空格,以此类推。注意坐标总是需要减 1;

rowIndex:我们所处的当前行的行数。我们从第 1 行开始时,rowIndex 等于 0,因为坐标需要减 1。当 helper(0) 调用 helper(1) 后,我们进入到了第 2 行,所以 rowIndex 在那时等于 1。

在 checkBoard() 方法里,我们做了两个检查,第一个是看每一列是不是只有一个皇后,第二个是看每一条斜线上是不是只有一个皇后。因为我们每多添加一名皇后时都会做一遍检查,所以我们在添加当前皇后前,盘面肯定是成立的。

因此,我们只需要看一下当前皇后对盘面的影响是否成立。也就是说,我们只需要检查当前皇后的纵坐标是不是独一无二的,以及当前皇后的两条斜线上有没有其他皇后就可以。斜线检查利用了纵坐标差和横坐标差的绝对值。

helper() 方法通过 for 循环依次尝试当前行的 n 个空格。如果把皇后放在第一个空格里成立,它会直接调用自己去往下一行(rowIndex+1 行),但是如果当前空格不成立,它会尝试下一个空格。

如果 n 个空格都不成立,它会后退到上一行的 for 循环里,继续尝试上一行的下一个空格。比如,如果第三行的 n 个空格都不成立,helper(2) 就会返回 helper(1),把第二个皇后换到第二行的下一个空格。

helper() 自我调用的方式可以被看作一个栈,或者一摞盘子,最后放上去的盘子总是第一个被取走的。如图 8 所示,每次我们调用 helper() 方法时,就像是放上去了一个盘子;

图 8:helper() 方法自我调用的顺序

如图 9 所示,每当 helper() 方法结束后,最上面的盘子会被取走,算法会自动返回到上一行。

图 9:helper() 方法回溯

如图 10 所示,我们不一定只将盘子放一次就取走,因为我们一直在尝试—碰壁—尝试—碰壁,所以真实的情况很可能是将盘子放上去,取走,再放上去,再取走,直到最后 helper(0) 被取走,意味着我们找到了答案之一。

图 10:helper() 方法的调用

n皇后问题python_N皇后问题—回溯算法经典例题相关推荐

  1. 贪心算法经典例题3:导弹发射问题

    贪心算法经典例题3:导弹发射问题 问题描述: 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一 ...

  2. n皇后问题(C语言 回溯算法)

    说明:N皇后问题是在n行n列的棋盘上放置N个皇后,使得皇后彼此之间不受攻击,其规则是任意两个皇后不在同一行,同一列和相同的对角线上. 算法思路:第i个皇后放在第i行,从第一个皇后开始,对每个皇后从其对 ...

  3. n皇后问题python_N皇后问题(python实现)

    问题描述 规则同8皇后问题, 但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大. 输入格式 一个8*8的棋盘. 输出格式 所能得到的最大数字和 样例输入 1 2 3 4 5 6 7 8 9 ...

  4. Leetcode回溯算法经典题目总结

    回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 "回溯" 返回,尝试别的路径.回溯法是一种选优搜索法,按选优条件向前搜索 ...

  5. 回溯算法经典问题-迷宫问题

    迷宫问题是一道经典的回溯算法问题,给定一个迷宫矩阵,矩阵中的1表示障碍,0表示可走通路,给定迷宫入口出口,要求寻找从入口穿过迷宫到达出口的所有路径,有则输出,无则给出提示.一本合格的数据结构教科书一般 ...

  6. python回溯算法_回溯算法经典问题及python代码实现

    2. 0-1背包问题 # 0-1 bag problem import sys def f(no, cur_mass, things, num): global cur_max if no == nu ...

  7. 贪心算法经典例题分析

    贪心经典例题 一.区间覆盖问题 例题:洛谷P1803 这是一个非常简单的区间覆盖问题. 针对此类问题,最佳的贪心算法应为将每一个区间的结束时间从小到大排序. 令所选的第 i 个区间的结束时间为 e[i ...

  8. 回溯算法经典案例 1 ——N皇后 Java

    问题分析 N 皇后问题研究的是如何将 N 个皇后放置在 N×N 的棋盘上,并且使皇后彼此之间不能相互攻击. 皇后的走法是:可以横直斜走,格数不限.因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇 ...

  9. 动态规划算法经典例题_c动态规划精简例题

    相信很多初学者,在刚刚接触动态规划,都花费了不少时间.网上有很多人写如何去做动态规划的题目,但是很少有整理适合初学者做的习题.这里是我自己考研期间,复习整理的.希望对大家有帮助. c动态规划精简例题 ...

最新文章

  1. 用友ERP供应链模块(一)----库存盘点
  2. Yann LeCun 最新发声:自监督+世界模型,让 AI 像人类与一样学习与推理
  3. stm32 DMA 配置 串口程序
  4. JVM—如何利用虚拟机栈进行函数调用?
  5. 请给你的短信验证码接口加上SSL双向验证
  6. 【转】EntityFramework使用Code First模式创建数据库控制生成单数形式的表名
  7. linux每日命令(20):find命令概览
  8. wxml、wxss、js 引入外部文件的方法
  9. 数学之美系列 1.3w字精简版阅读笔记
  10. Unity URP/SRP 渲染管线浅入深出【匠】
  11. 拿到acm铜奖可以去大厂吗?
  12. 易辅客栈 从零学辅助_如何从零启动辅助项目
  13. 一文看懂量子十问(上篇)
  14. Windows Live Writer插件开发经验
  15. 64qam带宽计算_烧脑:5G 理论峰值速率是怎么计算的?
  16. Assets, Objects and serialization Assets, Objects与序列化 最佳实践系列3
  17. 对android layout_wight属性和weight_sum属性的深入理解
  18. 9.5 隐函数求导法则
  19. Teamviewer删除账号
  20. 播放器可以完成:开机自动打开指定网页,自动运行浏览器打开指定的网页。

热门文章

  1. spdlog和fork冲突问题
  2. 同花顺股票交易接口调试说明
  3. c语言10种复合赋值运算符,C语言复合赋值运算符
  4. 如何打开VSCode的lauch.json文件
  5. 关于如何查询一个期刊是否被SCI或其他机构收录
  6. graph学习,GNN综述
  7. 塔木德分财产 思路+图片易懂 2022
  8. 中国IT外包追赶印度机遇来临 外包业面临转型
  9. Tomcat项目卡件情况:调整启动参数
  10. Redis 分布式锁|从青铜到钻石的五种演进方案