难度
困难
题目
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
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 个皇后,使得它们互相不能攻击(即不能在同一行、同一列或同一斜线上),N 皇后问题的一种解题思路,采用回溯算法:
- 初始化一个 n * n的棋盘,默认都为0,表示未放棋,初始化三个集合,分别记录列,正对角线,反对角线是否放置棋。
- 递归地尝试每一行,并且横向遍历每一列,检查当前位置是否符合规则,即:
- 检查该点所在的列、正对角线、反对角线是否已经放置棋,如果未放置则该点可以放置棋。
- 正对角线判断规则,从左上到右下 同一条斜线上的每个位置满足行下标与列下标之差相等
- 反对角线判断规则,从右上到左下 同一条斜线上的每个位置满足行下标与列下标之和相等
- 当递归的行数达到边界时,退出递归。
代码
from typing import Listclass Solution:def solveNQueens(self, n):self.index = 1self.n = n# 初始化 n * n的棋盘,默认都为0,未放棋self.chessboard = [[0] * self.n for i in range(self.n)]# 记录已经放了棋的列self.col = set()# 记录已经放了棋的正对角线self.d1 = set()# 记录已经放了棋的反对角线self.d2 = set()self.result = []self.dfs(0)def dfs(self, row):# 检查每一行能否放棋if row >= self.n:# 当行数到达边界时,打印结果print(self.index, '----------------------')self.printq()self.index += 1self.result.append(self.chessboard)return self.result# 扫描每一列元素for col in range(self.n):# d1 表示 从右上到左下 同一条斜线上的每个位置满足行下标与列下标之和相等# d2 表示 从左上到右下 同一条斜线上的每个位置满足行下标与列下标之差相等d1, d2 = col + row, col - row# 检查列是否被占用if col in self.col:continue# 检查正对角线是否被占用 列-行if d1 in self.d1:continue# 检查反对角线是否被占用 列+行if d2 in self.d2:continue# 放置皇后self.chessboard[row][col] = 1# 标记self.col.add(col)self.d1.add(d1)self.d2.add(d2)# 纵向遍历,检查下一行self.dfs(row + 1)# 回溯self.col.remove(col)self.d1.remove(d1)self.d2.remove(d2)self.chessboard[row][col] = 0def printq(self):for i in self.chessboard:x = []for j in i:if j == 1:x.append('Q ')else:x.append('* ')print(''.join(x))def prt(data):for line in data:print(line)# 第二种解法
def NQ(data, row, n):if row == n:prt(data)return# 检查row当前行中所有列for i in range(n):# 检查当前节点是否可以放棋if check_point(data, row, i):data[row][i] = 1# 进下一行,中所有的列NQ(data, row + 1, n)data[row][i] = 0def check_point(data, row, col):# 检查 (row, col) 点的位置是否可以放n = len(data[0])# 检查当前点所在的列中,是否已经放了棋for i in range(row):if data[i][col] == 1:return False# 当前行for i in range(row):# 所有列for j in range(n):# 检查两个对角线中是否已经放了棋if i + j == row + col and data[i][j] == 1:return Falseif i - j == row - col and data[i][j] == 1:return Falsereturn Truedef main(n):data = [[0] * n for _ in range(n)]NQ(data, 0, n)if __name__ == '__main__':s = Solution()res = s.solveNQueens(4)# print(res)main(4)