目录
任务描述
本关任务:编写程序实现 N 皇后问题。
相关知识
为了完成本关任务,你需要掌握: N 皇后问题的思想及实现。
N 皇后问题
在 N 行 N 列的国际象棋上摆放 N 个皇后,使其不能互相攻击(即任意两个皇后都不能处于同一行、同一列或同一斜线上)。请问有多少种摆法,以及如何摆放这些皇后。这就是经典的 N 皇后问题。
图1 N 皇后问题
问题分析
根据问题求解的方法,我们先将问题表示出来:
-
状态:棋盘上 0−N 个皇后的任一摆放都是一个状态;
-
初始状态:N×N 的棋盘上没有皇后;
-
算子:在棋盘上的任一空格摆放一个皇后;
-
目标:N个皇后都在棋盘上,并且无法互相攻击。
设棋盘某一位置为(i,j),将第i+1行的N个空格位置都作为(i,j)的后继结点,那么N×N的棋盘则可以通过一个根结点连接为一棵N叉搜索树,棋盘第i行对应搜索树深度为i的结点层,树的根结点到叶子节点的路径序列就是一个棋盘的状态序列,应用深度优先搜索算法可以非常方便的求解。
由于状态空间会随着N呈现出爆炸式的增长,搜索会非常浪费时间,我们可以加一些限制条件,这样可以排除很多不必要地搜索:
-
每一行只能放置一个皇后,那么对这一行其余的结点就不需要放置皇后了,也就是上面的建树过程,只以下一行棋盘空格作为后继结点,忽略本行其余空格;
-
每一列只能放置一个皇后,下次搜索时就不要搜索已经放置过皇后的列了;
-
每一斜对角线只能放置一个皇后。
棋盘的行可以用树的深度来表达,因此每一行只有一个皇后,她们不会同行。棋盘的列可以用一个长度为N的一维数组来记录是否放置过皇后。观察棋盘的行和列的特点。
编程要求
本关的编程任务是,补全右侧编辑 Begin-End 区间的代码,使函数 solveNQueens 和函数 DFS 实现指定功能 ,具体要求如下:
-
在 solveNQueens 中,利用 DFS 函数实现N皇后问题求解,并返回方案总数;
-
在 DFS 中,利用深度优先搜索算法的思想,递归求解N皇后问题。
测试说明
平台会对你编写的代码进行测试:
测试输入:1
预期输出:1
输入格式:整数,表示皇后数量。
输出格式:整数,表示 N 皇后放置方案总数。
代码部分
# -*- 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皇后放置方案数
"""
#请在这里补充代码,完成本关任务
#********** Begin **********#
self.vis = [[0 for j in range(50)] for i in range(3)]
self.DFS(1,self.n) # 递归
return self.ans
#********** End **********#
def DFS(self, row, n):
"""深度优先搜索N皇后问题的解空间
:type: row: int #NxN棋盘的第row行
:type: n: int #皇后数量n
:rtype: None #无返回值
"""
#请在这里补充代码,完成本关任务
#********** Begin **********#
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)