N皇后问题
N皇后问题是国际象棋的扩展,在N×N的棋盘上放置N个皇后,使得每一个皇后都不会攻击到其余的任一皇后(皇后可以攻击和它在同一行、同一列或同一对角线上的任何棋子)。
深度优先搜索算法
深度优先搜索 Depth-First-Search 总是扩展搜索树的当前边缘结点中最深的节点。搜索很快的推进到搜索树的最深处的结点,该结点称之为叶子结点,它没有后继,当这些结点扩展完之后,就从边缘结点中去掉,然后回溯到下一个未扩展后继的深度稍浅的结点,搜索过程如下图所示。
与宽度优先搜索使用 FIFO 队列不同,深度优先搜索使用 LIFO, Last In First Out 的队列结构,即最新生成的结点最早被选择扩展,因此,为了实现方便,深度优先搜索算法一般抛弃 LIFO 队列而采用递归式结构实现。
求解思路:
棋盘的行可以用树的深度来表达,因此每一行只有一个皇后,她们不会同行。棋盘的列可以用一个长度为N的一维数组来记录是否放置过皇后。观察棋盘的行和列的特点,可以发现如下规律:
棋盘的左斜线\可以用一个数字表达,因此所有的左斜线可以用一个长度为2N−1的一维数组来记录是否放置过皇后,同理,右斜线/也可以用一个长度为2N−1的一维数组来记录是否放置过皇后。
具体实现
代码给了详细的注释
```python
# -*- coding:utf-8 -*-
class Solution:
def __init__(self, n=0):
self.vis = [[]] #用于标记是否存在皇后的二维列表(初始值全为0)
self.ans = 0 #用于存储答案(N皇后方案数,初始值0)
self.n = n #用于存储皇后数量n
def solveNQueens(self):
"""求解N皇后问题(调用self.DFS函数)
:rtype: self.ans: int #返回N皇后放置方案数
"""
# 产生一个3行50列的数组,第一行代表左斜线,第二行代表所在的列,第三行代表有索引,初始值都为0
self.vis = [[0 for j in range(50)] for i in range(3)]
self.DFS(1,self.n) # 递归
return self.ans
def DFS(self, row, n):
"""深度优先搜索N皇后问题的解空间
:type: row: int #NxN棋盘的第row行
:type: n: int #皇后数量n
:rtype: None #无返回值
"""
if row == n+1: # 行数大于皇后的个数,说明这个方案可行,方案数加1,递归结束(递归结束条件),
self.ans += 1
return
for i in range(1,n+1,1):
# vis[0][row-i+n] == 0 row-i即行的索引减去列的索引,左斜线等于0,左斜线没有放置皇后,+n是为了防止产生负数
# vis[1][i] == 0 第i列为0
# vis[2][row+i] == 0 即所在的右斜线上并没有存放皇后,row+i即行索引加上列索引,同一右斜线的行索引加列索引所得的值相等。
if self.vis[0][row-i+n] == 0 and self.vis[1][i] == 0 and self.vis[2][row+i] == 0: # 第row行第i列可以存放皇后
self.vis[0][row-i+n] = self.vis[1][i] = self.vis[2][row+i] = 1 # 将对应的列,左斜线、右斜线都置为1
self.DFS(row+1,n) # 存放下一个皇后
# 如果下一个皇后的位置无法正确放置,则调整当前皇后的放置位置
self.vis[0][row-i+n] = self.vis[1][i] = self.vis[2][row+i] = 0
# 继续本次循环,知道找到本行的列中有合适的存放位置为止,如果本行无合适位置,则继续返回上一层
if __name__ == '__main__':
queen = Solution()
queen.__init__(8)
ans = queen.solveNQueens()
print(ans)
以八皇后为例,最后输出92种方案数。