原题地址
官方解答
第二个叫做 回溯法.
我们来想象一下,当在棋盘上放置了几个皇后且不会相互攻击。但是选择的方案不是最优的,因为无法放置下一个皇后。此时我们该怎么做?回溯。意思是回退一步,来改变最后放置皇后的位置并且接着往下放置。如果还是不行,再 回溯。
方法1:回溯
在建立算法之前,我们来考虑两个有用的细节。
一行只可能有一个皇后且一列也只可能有一个皇后。
这意味着没有必要再棋盘上考虑所有的方格。只需要按列循环即可。
对于所有的主对角线有 行号 + 列号 = 常数,对于所有的次对角线有 行号 - 列号 = 常数.
这可以让我们标记已经在攻击范围下的对角线并且检查一个方格 (行号, 列号) 是否处在攻击位置。
现在已经可以写回溯函数 backtrack(row = 0).
从第一个 row = 0 开始.
循环列并且试图在每个 column 中放置皇后.
如果方格 (row, column) 不在攻击范围内
在 (row, column) 方格上放置皇后。
排除对应行,列和两个对角线的位置。
If 所有的行被考虑过,row == N
意味着我们找到了一个解
Else
继续考虑接下来的皇后放置 backtrack(row + 1).
回溯:将在 (row, column) 方格的皇后移除.
去题解里看一下官方给的幻灯片!!!
修改了原题解中的主次对角线代码错误问题(row+col和row-col)
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
#用于存放皇后位置(row,col)
queens = set()
#用于保存输出
output = []
#用于存放queen所在列的情况
cols = [0]*n
#主对角线 和为定值 通过这个定值确定一主对角线位置
hill_diagonals = [0]*(2*n-1)
#次对角线 差为定值 通过这个定值确定一次对角线位置
dale_diagonals = [0]*(2*n-1)
def place_queen(row,col):
#加入皇后的位置而且把相应列,主次对角线置为不能再皇后
queens.add((row,col))
cols[col] = 1
hill_diagonals[row+col]=1
dale_diagonals[row-col]=1
def remove_queen(row,col):
#删除皇后的位置而且把相应列,主次对角线置为可以放皇后
queens.remove((row,col))
cols[col] = 0
hill_diagonals[row+col]=0
dale_diagonals[row-col]=0
def could_place(row,col):
#如果这个位置(row,col),列上有皇后或所在主次对角线有皇后,返回False,反之True
return not (cols[col]+hill_diagonals[row+col]+dale_diagonals[row-col])
def add_solution():
#用于格式化保存答案
#每一个solution是一种解
solution = []
for _,col in sorted(queens):
solution.append('.'*col+'Q'+'.'*(n-col-1))
output.append(solution)
def backtrack(row = 0):#从第一行开始
#遍历所有列
for col in range(n):
#如果这个行列可以放皇后
if could_place(row,col):
#就放皇后
place_queen(row,col)
#如果到了最后一行
if row+1 == n:
#则这个排列成立,保存一个解
add_solution()
#如果不是最后一行
else :
#进入下一行并判断
backtrack(row+1)
#这个位置皇后状态判断完则把她存在的痕迹抹去
remove_queen(row,col)
backtrack()
return output