Leetcode-剪枝

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-27 12:31:22

51. N皇后 https://leetcode-cn.com/problems/n-queens/

皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

解:

dfs + 剪枝,枚举每个行。注意在做回溯之前,要把当前放置的皇后拿掉,把其造成影响的标识位都消除。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        def could_place(row, col):
            # row这一行是没有放置过的行,要检查col这一列、(row,col)所占两条对角线有没有被放置过,如果都没有,(row,col)可以放皇后
            return not (cols[col]+hill_diagonals[row-col]+\
                        dale_diagonals[row+col]) 
        
        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 add_solution():
            # 如果找到一个解,按要求记录下来
            solution = []
            for _, col in sorted(queens):
                solution.append('.'*col + 'Q' + '.'*(n-col-1))
            output.append(solution)
        
        def dfs(row):
            # 从第一行row=0开始放置皇后,放到n-1行
            for col in range(n):  # 对于确定的row,遍历所有列col
                if could_place(row, col):
                    place_queen(row, col)  # 如果(row, col)可以放皇后,就放
                    if row == n-1:  # 如果已经放了最后一个,说明找到一个解
                        add_solution()
                    else:  # 没有放到最后一个的话
                        dfs(row+1)  # 去找row行之后所有可能的放置解法
                    remove_queen(row, col)  # 不管是哪种情况都要回溯,移除当前皇后,进入(row, col+1) 的情况
            
        cols = [0] * n
        hill_diagonals = [0] * (2 * n -1)
        dale_diagonals = [0] * (2 * n -1)
        queens = set()
        output = []
        
        dfs(0)
        return output

  

52. N皇后ii https://leetcode-cn.com/problems/n-queens-ii/

给定一个整数 n,返回 n 皇后不同的解决方案的数量。

解:

跟#51基本相同,修改一下最后返回值的处理即可

class Solution:
    def totalNQueens(self, n: int) -> int:
        def could_place(row, col):
            # row这一行是没有放置过的行,要检查col这一列、(row,col)所占两条对角线有没有被放置过,如果都没有,(row,col)可以放皇后
            return not (cols[col]+hill_diagonals[row-col]+\
                        dale_diagonals[row+col]) 
        
        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 dfs(row):
            nonlocal res
            for col in range(n):
                if could_place(row, col):
                    place_queen(row, col)
                    if row == n-1:
                        res += 1
                    else:
                        dfs(row+1)
                    remove_queen(row, col)  # 把刚才放置的拿掉才能回溯
                    
        cols = [0] * n
        hill_diagonals = [0] * (2 * n - 1)
        dale_diagonals = [0] * (2 * n - 1)
        queens = set()
        res = 0
        dfs(0)
        return res

  

36. 有效的数独 https://leetcode-cn.com/problems/valid-sudoku/

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

数独部分空格内已填入了数字,空白格用 '.' 表示。

 说明:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。

解:

两层嵌套循环遍历即可,分别对行、列、子数独用 n=9 个哈希表(其中key为1~9)来存所有已经遇到过的值,如果某个key的value大于1的话说明数独无效。

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        # 9行,9列,9个子数独
        rows = [{} for _ in range(9)]  # 每个哈希表存,列表下标对应行的,已填数字情况 
        cols = [{} for _ in range(9)]
        boxes = [{} for _ in range(9)]  # 子数独编号为boxes的下标,从上到下从左到右索引
        
        for i in range(9):
            for j in range(9):
                num = board[i][j]
                if num != '.':     # 如果某个位置已经填入数
                    num = int(num)
                    box_idx = (i//3)*3 + j//3   # 当前位置所处的子数独索引
                    
                    rows[i][num] = rows[i].get(num, 0) + 1  # 先保留当前的数,在哈希表中保存
                    cols[j][num] = cols[j].get(num, 0) + 1
                    boxes[box_idx][num] = boxes[box_idx].get(num, 0) + 1
                    
                    # 检查如果保留当前数的话,是否合法
                    if rows[i][num] > 1 or cols[j][num] > 1 or boxes[box_idx][num] > 1:
                        return False
        return True

 

37. 解数独 https://leetcode-cn.com/problems/sudoku-solver/

编写一个程序,通过已填充的空格来解决数独问题。

解:

dfs, 枚举每个空格。除了普通的dfs思路,还需要一些剪枝的条件来加速。比如,先枚举可选项少的空格、

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!