zoukankan      html  css  js  c++  java
  • Leetcode-剪枝

    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, 枚举每个空格。

    from collections import defaultdict
    class Solution:
        def solveSudoku(self, board: List[List[str]]) -> None:
            """
            Do not return anything, modify board in-place instead.
            """
            def could_place(d, row, col):
                """
                Check if one could place a number d in (row, col) cell
                """
                return not (d in rows[row] or d in columns[col] or 
                        d in boxes[box_index(row, col)])
            
            def place_number(d, row, col):
                """
                Place a number d in (row, col) cell
                """
                rows[row][d] += 1
                columns[col][d] += 1
                boxes[box_index(row, col)][d] += 1
                board[row][col] = str(d)
                
            def remove_number(d, row, col):
                """
                Remove a number which didn't lead 
                to a solution
                """
                del rows[row][d]
                del columns[col][d]
                del boxes[box_index(row, col)][d]
                board[row][col] = '.'    
                
            def place_next_numbers(row, col):
                """
                Call backtrack function in recursion
                to continue to place numbers
                till the moment we have a solution
                """
                # if we're in the last cell
                # that means we have the solution
                if col == N - 1 and row == N - 1:
                    nonlocal sudoku_solved
                    sudoku_solved = True
                #if not yet    
                else:
                    # if we're in the end of the row
                    # go to the next row
                    if col == N - 1:
                        backtrack(row + 1, 0)
                    # go to the next column
                    else:
                        backtrack(row, col + 1)
                    
                    
            def backtrack(row = 0, col = 0):
                """
                Backtracking
                """
                # if the cell is empty
                if board[row][col] == '.':
                    # iterate over all numbers from 1 to 9
                    for d in range(1, 10):
                        if could_place(d, row, col):
                            place_number(d, row, col)
                            place_next_numbers(row, col)
                            # if sudoku is solved, there is no need to backtrack
                            # since the single unique solution is promised
                            if not sudoku_solved:
                                remove_number(d, row, col)
                else:
                    place_next_numbers(row, col)
                        
            # box size
            n = 3
            # row size
            N = n * n
            # lambda function to compute box index
            box_index = lambda row, col: (row // n ) * n + col // n
            
            # init rows, columns and boxes
            rows = [defaultdict(int) for i in range(N)]
            columns = [defaultdict(int) for i in range(N)]
            boxes = [defaultdict(int) for i in range(N)]
            for i in range(N):
                for j in range(N):
                    if board[i][j] != '.': 
                        d = int(board[i][j])
                        place_number(d, i, j)
            
            sudoku_solved = False
            backtrack()
    

      

    除了普通的dfs,还可以加一些剪枝的条件来加速。先枚举可选项少的空格:预处理,先遍历一边格子,找出每个空格的可选数,并排序,dfs搜索时就从可选数最少的空格开始。

  • 相关阅读:
    百度之星资格赛1001——找规律——大搬家
    HDU1025——LIS——Constructing Roads In JGShining's Kingdom
    DP(递归打印路径) UVA 662 Fast Food
    递推DP UVA 607 Scheduling Lectures
    递推DP UVA 590 Always on the run
    递推DP UVA 473 Raucous Rockers
    博弈 HDOJ 4371 Alice and Bob
    DFS(深度) hihoCoder挑战赛14 B 赛车
    Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2)
    DP(DAG) UVA 437 The Tower of Babylon
  • 原文地址:https://www.cnblogs.com/chaojunwang-ml/p/11363167.html
Copyright © 2011-2022 走看看