[51][困难][回溯] N皇后
题目描述
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[["Q"]]
提示:
1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。
解题思路
N皇后是典型的回溯问题. N个皇后摆放在棋盘上, 相互之间不能攻击, 因此我们需要尝试依次摆放每个棋子, 前面摆放的棋子都后续的摆放产生了限制.
为了快速查询当前位置能否摆放棋子, 我们记录每行, 每列, 每条对角线和反对角线是否已经被占用.
行和列容易记录, 两种对角线的记录方式绕一些. 对于n * n的棋盘, 它的对角线和反对角线各有2n - 1条, 其中:
同一条对角线位置的横纵坐标相减恒定, 即
i-j
不变反对角线的横纵坐标之和恒定, 即
i+j
不变
这样我们就可以创建4个数组, 分别记录每行, 每列, 每条对角线和反对角线是否已经被占用.
遍历时从某个坐标出发, 向四周搜索, 是最简单的搜索方法. 但这种方法复杂度高, 最坏的情况下, 每个皇后都要遍历所有棋盘位置才能确定. 提交后超时.
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
if n == 1:
return [['Q']]
board = [['.'] * n for _ in range(n)]
row = [False] * n
col = [False] * n
diagonal = {i: False for i in range(-(n - 1), n)}
back_diagonal = {i: False for i in range((n - 1) * 2 + 1)}
seen = [[False] * n for _ in range(n)]
res = set()
def dfs(i, j, count):
if count == n:
res.add('|'.join([''.join(t) for t in board]))
return
seen[i][j] = True
if not row[i] and not col[j] and not diagonal[i - j] and not back_diagonal[i + j]:
row[i] = col[j] = diagonal[i - j] = back_diagonal[i + j] = True
board[i][j] = 'Q'
if 0 <= i - 1 < n and not seen[i - 1][j]:
dfs(i - 1, j, count + 1)
if 0 <= i + 1 < n and not seen[i + 1][j]:
dfs(i + 1, j, count + 1)
if 0 <= j - 1 < n and not seen[i][j - 1]:
dfs(i, j - 1, count + 1)
if 0 <= j + 1 < n and not seen[i][j + 1]:
dfs(i, j + 1, count + 1)
board[i][j] = '.'
row[i] = col[j] = diagonal[i - j] = back_diagonal[i + j] = False
if 0 <= i - 1 < n and not seen[i - 1][j]:
dfs(i - 1, j, count)
if 0 <= i + 1 < n and not seen[i + 1][j]:
dfs(i + 1, j, count)
if 0 <= j - 1 < n and not seen[i][j - 1]:
dfs(i, j - 1, count)
if 0 <= j + 1 < n and not seen[i][j + 1]:
dfs(i, j + 1, count)
seen[i][j] = False
dfs(0, 0, 0)
return [t.split('|') for t in res]
而我们知道, 每行有且只能有一个皇后, 因此可以按行探索, 每行内搜索所有可能摆放皇后的位置, 遍历完毕后, 探索下一行, 直到所有行探索完毕, 也就得到了所有可能的结果. 因此我们不再需要记录每一行是否被占用了, 而是记录每一行对应的皇后棋子摆放列的索引.
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
row = [-1] * n
col = [False] * n
diagonal = {i: False for i in range(-(n - 1), n)}
back_diagonal = {i: False for i in range((n - 1) * 2 + 1)}
res = []
def dfs(i):
if i == n:
t_res = []
for k in range(n):
tmp = ['.'] * n
tmp[row[k]] = 'Q'
t_res.append(''.join(tmp))
res.append(t_res)
return
for j in range(n):
if col[j] or diagonal[i - j] or back_diagonal[i + j]:
continue
row[i] = j
col[j] = diagonal[i - j] = back_diagonal[i + j] = True
dfs(i + 1)
col[j] = diagonal[i - j] = back_diagonal[i + j] = False
dfs(0)
return res
参考资料
最后更新于
这有帮助吗?