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搜索时就从可选数最少的空格开始。

  • 相关阅读:
    数据结构、算法、及线性表总结
    第二次博客作业: 函数+进制转换器v1.0beta
    c语言文件
    Oracle中Left Outer Join和外关联(+)的区别
    Oracle中trunc函数、round 函数、ceil函数和floor 函数的使用
    Oracle 表之间的连接 JOIN
    Oracle TRUNCATE语法
    使用Content editor webpart 为NewForm增加默认值
    Metadata serviceTaxonomyHiddenList 权限
    SQL server总是不能远程连接
  • 原文地址:https://www.cnblogs.com/chaojunwang-ml/p/11363167.html
Copyright © 2011-2022 走看看