每天一道算法题:51. N 皇后

难度

困难

题目

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n_ _皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
每天一道算法题:51. N 皇后_第1张图片

示例 1:

输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[[“Q”]]

提示:

1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

思路

N 皇后问题是一个经典的回溯算法问题,要求在 N×N 的棋盘上放置 N 个皇后,使得它们互相不能攻击(即不能在同一行、同一列或同一斜线上),N 皇后问题的一种解题思路,采用回溯算法:

  1. 初始化一个 n * n的棋盘,默认都为0,表示未放棋,初始化三个集合,分别记录列,正对角线,反对角线是否放置棋。
  2. 递归地尝试每一行,并且横向遍历每一列,检查当前位置是否符合规则,即:
  3. 检查该点所在的列、正对角线、反对角线是否已经放置棋,如果未放置则该点可以放置棋。
  4. 正对角线判断规则,从左上到右下 同一条斜线上的每个位置满足行下标与列下标之差相等
  5. 反对角线判断规则,从右上到左下 同一条斜线上的每个位置满足行下标与列下标之和相等
  6. 当递归的行数达到边界时,退出递归。

代码

from typing import List


class Solution:
    def solveNQueens(self, n):
        self.index = 1
        self.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 += 1
            self.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] = 0

    def 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] = 0


def 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 False
            if i - j == row - col and data[i][j] == 1:
                return False
    return True


def 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)

你可能感兴趣的:(算法实战,力扣)