题目
在n*n格的棋盘上放置彼此不受攻击的n个皇后。由于皇后可以攻击与之处于同一行或同一列或在同一斜线上的棋子。n皇后问题等价于在n*n的棋盘上放置n个皇后,任何两个皇后不放在同一列或同一行或同一斜线上。
DFS + 剪枝法
解法分析:最粗暴的做法是每一行遍历列,然后每次和前面的所有元素判断一下是不是互相攻击。复杂度太高。DFS+剪枝的做法是,从上往下遍历层,每一层遍历列,由于每一撇上的元素col + row都是相等的,每一捺上的元素 col - row都是相等的,所以对于每一个当前可行的元素,把它的col、col + row、col - row存储到各自的集合里面,新的元素如果这三个数都不在对应集合里面,那么是可行的。遍历到第n层(最后一层是n-1层)直接把当前的状态放到result里面。
def n_queens(n):
if n < 1:
return []
result = []
cols, pie, na = set(), set(), set()
def dfs(n, row, cur_state):
if row >= n:
result.append(cur_state)
return
for col in range(n):
if col in cols or row + col in pie or row - col in na:
continue # 不合适,跳过
# 更新flag
cols.add(col)
pie.add(row + col)
na.add(row - col)
dfs(n, row+1, cur_state+[col])
# 恢复现场
cols.remove(col)
pie.remove(row + col)
na.remove(row - col)
def _generate_result(n):
board = []
for res in 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)]
dfs(n, 0, [])
return _generate_result(n)
print(n_queens(4))
简洁版。因为flag是dfs()函数的参数,因此不需要恢复现场。最后打印的时候采用二维的列表推导式,简化代码。
def n_queens(n):
if n < 1:
return []
result = []
def dfs(cols, xy_dif, xy_sum):
row = len(cols)
if row == n:
result.append(cols)
return None
for col in range(n):
if col not in cols and row - col not in xy_dif and row + col not in xy_sum:
dfs(cols + [col], xy_dif + [row - col], xy_sum + [row + col])
dfs([], [], [])
return [["."*i + "Q" + "."*(n-i-1) for i in res] for res in result]
print(n_queens(4))
回溯法
回溯算法的解释如下:
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
回溯法可以看作一种DFS。对于某一个搜索树来说(搜索树是起记录路径和状态判断的作用),回溯和DFS,其主要的区别是,回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。
回溯法和DFS很相似,这里不再展开了。
位运算解法(最高效)
这是n皇后问题的最高效解法。
用cols、pie、na是三个数字,其二进制表示之前的列、撇和捺上的皇后,0表示没有,1表示有。
bits =(~(cols | pie | na))&((1<< n)-1)是用来得到当前的空位的,前面三个数相或之后得到的0位置是可以放皇后的,取反之后的1位置是可以放皇后的。由于一个数有很多位,我们只需要后n位,所以与上后面的((1<< n)-1)。
bits &-bits可以得到最低位的1。
调用下一层dfs的时候,row直接加1,col或当前的列即可,pie或上当前列之后要左移一位,na或上当前列后要右移一位。
bits &(bits -1)作用是去掉最低位的1。
def n_queens(n):
if n < 1:
return []
count = [0]
def dfs(n, row, cols, pie, na):
if row >= n:
count[0] = count[0] + 1
return
bits = (~(cols | pie | na)) & ((1 << n) - 1)
while bits:
p = bits & -bits
dfs(n, row+1, cols | p, (pie | p) << 1, (na | p) >> 1)
bits = bits & (bits - 1)
dfs(n, 0, 0, 0 ,0)
return count[0]
print(n_queens(4))