N皇后问题
作用
N皇后问题是经典的回溯算法问题,用于解决在N x N的棋盘上放置N个皇后问题。N皇后问题有多解,即对于任意满足N > 3的正整数N,N皇后问题都有解。
N皇后问题与其他回溯算法问题类似,是为了求出所有的解,而非一种满足条件即可的问题。因此,N皇后问题的解法需要遍历所有的可能性,只有当问题满足条件时才会将解保存并继续向下搜索,否则回退到上一层继续寻找其他可能解。
N皇后问题常常被用于评价算法的效率和复杂度,因此熟练掌握N皇后问题的解法对于程序员来说是非常重要的。
使用方法
N皇后问题的解法基本上可以分为两类,一类是通过深度优先搜索完成,另一类则是通过分治法和位运算完成。
深度优先搜索
对于深度优先搜索,我们可以使用回溯法解决N皇后问题。具体步骤如下:
-
从棋盘的第一行开始,以列为单位一列一列地搜索(可以简化为从每行开始搜索,因为任选一行开始搜索,有置换对称性)。定义一个列表记录当前所有已放置皇后所在的列号。
-
在当前行从第一列开始搜索,对于每一列,判断该列是否合法,即在当前列放置皇后是否会受到当前所有已放置皇后的攻击。如果不合法,直接跳过本次循环,进入下一次循环;如果合法,将该列号添加到已放置皇后的列表中,并进入下一行搜索。
-
重复2,直到搜索完最后一行。此时所有皇后已经放置在棋盘上,保存这个解,并回退到上一层。然后尝试在当前行的下一列寻找新的解。
-
重复2、3直到搜索完所有的可能性。
下面是python代码示例:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
self.result = []
self.cols = set()
self.pie = set()
self.na = set()
self.dfs([], 0, n)
return self._generate_result(n)
def dfs(self, cur_state, row, n):
if row == n:
self.result.append(cur_state)
return
for col in range(n):
if col in self.cols or row + col in self.pie or row - col in self.na:
continue
self.cols.add(col)
self.pie.add(row + col)
self.na.add(row - col)
self.dfs(cur_state + [col], row + 1, n)
self.cols.remove(col)
self.pie.remove(row + col)
self.na.remove(row - col)
def _generate_result(self, n):
board = []
for res in self.result:
for i in res:
board.append('.' * i + 'Q' + '.' * (n - i - 1))
return [board[i: i + n] for i in range(0, len(board), n)]
分治法和位运算
下面是通过分治法和位运算完成N皇后问题的示例。
class Solution:
def totalNQueens(self, n: int) -> int:
if n < 1:
return 0
self.count = 0
self.dfs(n, 0, 0, 0, 0)
return self.count
def dfs(self, n, row, cols, pie, na):
if row == n:
self.count += 1
return
bits = (~(cols | pie | na)) & ((1 << n) - 1)
while bits:
p = bits & -bits
bits = bits & (bits - 1)
self.dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1)
示例说明
示例一:深度优先搜索
假设我们要在8×8的棋盘上放置8个皇后,并要求皇后之间不能互相攻击。下面是程序的运行结果:
[
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
示例二:分治法和位运算
假设我们要求解4皇后问题。下面是程序的运行结果:
2
总结
N皇后问题是经典的回溯算法问题,解决方法有深度优先搜索和分治法和位运算两种。熟练掌握N皇后问题的解法对于程序员来说是非常重要的。