2018-12-14

girl-1162060_1920.jpg

LeetCode 51. N-Queens

Description

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

8-queens.png

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

Example:

Input: 4
Output: [
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],

["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.

描述

n-queens难题是将n个皇后放置在n×n棋盘上的问题,使得没有两个皇后互相攻击。

给定整数n,返回n-queens拼图的所有不同解。

每个解决方案都包含n-queens位置的独特拼图位置,其中"Q"和"." 两者分别表示女王和空白.

解题

题意理解

n皇后问题,即要求将n个皇后放置在n*n的棋盘(以下称矩阵)中,使得任意一个皇后都不能攻击其她皇后,皇后能够攻击的位置是皇后所在的一整行,所在的一整列,所在的两条对角线。要求皇后不能互相攻击,即是要求任意一个皇后,她所在位置的行,列,两条对角线没有其他皇后.

思路

  • 此题目需要用到递归
  • n皇后问题,关键是要确定哪些位置可以放置,哪些位置不能放置.
  • 根据题意,矩阵的每一行只能有一个皇后,每一列也只能一个皇后.
  • 我们每一行每一行的寻找,看一行的哪个位置可以放置,为了确定这个位置是否可以放置皇后.
  • 我们需要检查:
  1. 这个位置所在的列是否有皇后.
  2. 这个位置所在的左对角线是否有皇后.
  3. 这个位置所在的右对角线是否有皇后.
  4. 可以不用检查行,因为我们是按行从每一个位置开始检查的,该行一定没有其他皇后.
nqueens.png
  • 如上图(以八皇后为例)。
  • 为此为了确定当前的列是否有皇后,我们维护一个数组表示当前的列是否被占用。
  • 对于左对角线和右对角线:对于正矩阵,如果有n条边,则有2*n-1条左对角线,2*n-1条有对角线(每列一条对角线,每行一条对角线,中间的对角线重叠).
  • 并且对于坐标为(row,col)的位置,其左对角线上的点满足row+col为定值(如下图),其右对角线上的点满足row+n-(col+1)为定值.
  • 可以这样理解:对于该点的左对角线,该对角线满足线上所有点到第一行的举例与到第一列的举例(不包括本身)之和[row+col]为定值;该点的右对角线满足对角线上所有点到一行的举例与最后一列的距离之和[row+n-(col+1)]为定值.
  • 于是我们用两个数组来表示对角线是否能放置皇后.
  • 可以参照维基百科的八皇后问题为例来理解皇后问题.
  • 皇后问题的解的个数是增加的非常快的,如下表格:
n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ..
U 1 0 0 1 2 1 6 12 46 92 341 1,787 9,233 45,752 ..
D 1 0 0 2 10 4 40 92 352 724 2,680 14,200 73,712 365,596 ..

有了以上分析,代码实现思路如下

  • 检查一行的元素是否能够放置皇后
  • 如果找到了一行的某个位置能够放置皇后,检查下一行
  • 找到最后一行截止
class Solution:
    # 把变量设置为对象的属性,可以方便递归函数的直接调用,使代码更加简洁
    def __init__(self):
        # 记录列的情况,表示是否被占用,Ture表示被占用,False表示没有被占用
        self.coloccupied = []
        # 记录左对角线的情况,表示左对角线是否被占用,Ture表示被占用,False表示没有被占用
        self.Left_diag = []
        # 记录又对角线的情况,表示右边的对角线是否被占用,Ture表示被占用,False表示没有被占用
        self.right_diag = []
        # 皇后的面板,所有的位置都初始化为'.'
        self.board = []
        # 矩阵维度,也就是棋盘的行列数
        self.rank = 0
        # 结果数组
        self.res = []

    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        # 初始化变量
        # 矩阵维度
        self.rank = n
        # 所有列的占用情况初始化为False,即没有被占用
        self.coloccupied = [False]*self.rank
        # 所有左对角线初始化为没有被占用
        self.Left_diag = [False]*(2*self.rank-1)
        # 所有有对角线初始化为没有被占用
        self.right_diag = [False]*(2*self.rank-1)
        # 皇后的位置矩阵是二维矩阵,初始化为"."表示没有被占用
        for _ in range(self.rank):
            self.board.append(['.']*self.rank)
        # 从第一行开始检查
        self.nQueen(0)
        for i in range(len(self.res)):
            self.res[i] = [''.join(subitem) for subitem in self.res[i]]
        return self.res

    # 检查当前位置是否被占用,被占用返回Ture,没有被占用返回False
    def isOccupied(self, row, col):
        # 当前位置所在的列,左对角线,右对角线只要有一个被占用,则该位置就被占用
        return self.coloccupied[col] or self.Left_diag[row+col] or self.right_diag[row+self.rank-col-1]

    def put(self, row, col, isput):
        # 该列是否被占用
        self.coloccupied[col] = isput
        # 该位置左对角线是否被占用
        self.Left_diag[row+col] = isput
        # 该位置右对角线是否被占用
        self.right_diag[row+self.rank-col-1] = isput
        # 如果是放置,则放入"Q",清空,放置"."
        self.board[row][col] = "Q" if isput else "."

    def nQueen(self, row):
        if row == self.rank:
            # 注意,这里需要用深拷贝
            self.res.append(copy.deepcopy(self.board))
            return
        for col in range(self.rank):
            # 如果被占用,进入下一个循环
            if self.isOccupied(row, col):
                continue
            # 在当前位置放置皇后
            self.put(row, col, True)
            # 进入下一层寻找
            self.nQueen(row+1)
            # 下一层寻找完成之后,释放当前位置,检查当前位置的下一个位置
            self.put(row, col, False)

源代码文件在这里
©本文首发于何睿的博客,欢迎转载,转载需保留文章来源,作者信息和本声明.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355