D30|重新安排行程+棋盘问题(N皇后+解数独)

332.重新安排行程

1.题目
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

2.复习前
1)构造字典来记录子节点,即终点位置;考虑按字母顺序排序来访问节点,于是对终点位置集合进行排序,依次访问(贪心算法)
2)遍历过的节点应当记录下已遍历
3)终止条件:遍历完所有飞行路径,此时包括重复节点
实现难点:没有想到按字母顺序排序来访问节点,而是得到所有路径后再做比较选出最合适的一条,这样多了一次结果遍历,且不能提前跳出循环

3.实现

class Solution:
    def __init__(self):
        self.res = []
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        n = len(tickets)
        ticket = defaultdict(list)
        state = False
        for item in tickets:
            ticket[item[0]].append(item[1])
        for key, value in ticket.items():
            ticket[key] = sorted(value)[::-1]
        def findticket(start, tmp):
            nonlocal state
            if state:
                return
            if len(tmp) == n + 1:
                self.res = tmp[:]
                state = True
                return
            airports = ticket[start]
            m = len(airports)
            for i in range(m-1, -1, -1):
                ll = airports[i]
                ticket[start].pop(i)
                tmp.append(ll)
                findticket(ll, tmp)
                tmp.pop()
                ticket[start].insert(i, ll)
        findticket("JFK", ["JFK"])
        return self.res

4.文章讲解
本题涉及图的概念,建议去复习:
如何识别图+构建图+图遍历方式+图结构特点

51. N皇后

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

2.复习前
只要能构建出树,这道题就很简单了!
虽然为棋盘问题,但是一维递归

3.实现

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]: 
        total = []
        record = []
        used = [0] * n
        def solvequeen(tmp, row):
            if len(tmp) == n:
                record.append(tmp[:])
                return
            for j in range(n):
                if used[j] == 1:
                    continue
                state = True
                # tmp保存每行的列值
                for i in range(len(tmp)):
                    if abs(j - tmp[i]) == abs(row - i):
                        state = False
                        break
                if not state:
                    continue
                tmp.append(j)
                used[j] = 1
                solvequeen(tmp, row+1)
                tmp.pop()
                used[j] = 0
        solvequeen([], 0)
        for point in record:
            res = []
            for i in range(n):
                s = '.'*(point[i]) + 'Q' + '.'*(n - point[i] - 1)
                res.append(s)
            total.append(res)
        return total

4.文章讲解
我的实现方式是通过得到需要Q皇后的位置之后再去改变的结果数组;
其实可以在遍历时直接对Q皇后位置改动

37. 解数独

1.题目
通过填充空格来解决数独问题

2.复习前
难点:如何去定义九宫格内数字不能重复;如何去访问每个节点

3.实现

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        used_row = [[0]*10 for _ in range(9)] # used[i][num]  针对行
        used_line = [[0]*10 for _ in range(9)] # used[j][num]  针对列
        block = [[[0]*10 for _a in range(3)] for _b in range(3)]
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j])
                    used_row[i][num] = 1
                    used_line[j][num] = 1
                    block[i // 3][j // 3][num] = 1
        print(block)
        def isvalid(row, col, num):
            row = (row // 3) * 3
            col = (col // 3) * 3
            for i in range(row, row + 3):
                for j in range(col, col + 3):  
                    if board[i][j] == str(num):
                        return False           
            return True
        def solve():
            for i in range(9):
                for j in range(9):
                    if board[i][j] != '.':
                            continue
                    for num in range(1, 10):
                        # if used_row[i][num] == 1 or used_line[j][num] == 1 or not isvalid(i, j, num):
                        #     continue
                        if used_row[i][num] == 1 or used_line[j][num] == 1 or block[i//3][j//3][num] == 1:
                            continue                        
                        board[i][j] = str(num)
                        used_row[i][num] = 1
                        used_line[j][num] = 1
                        block[i//3][j//3][num] = 1
                        if solve():
                            return True
                        board[i][j] = '.'
                        used_row[i][num] = 0
                        used_line[j][num] = 0
                        block[i//3][j//3][num] = 0
                    return False
            return True
        solve()

4.文章讲解
总结:
1)和N皇后一样,可以在遍历时直接对board数组原地修改;也正是直接修改从而在循环时无需传入参数就可实现对所有空白处进行访问
2)用以往的数组记录方式来标记已存在的数字是可行的,但对于Python如何用列表去定义二维、三维数组的方式不太熟悉

a1 = [0]*2  # 通过✖对[]中的元素进行重复得到[0, 0]
a2 = []*2  # 仍是[],因为此时列表内没有添加元素
a3 = [[] for _ in range(2)] # 借助迭代器,相当于对外层[]中元素[]去反复添加[]
# 等效于 a3=[] for _ in range(2): a3.append([])
# 创建三维数组
a4 = [[[0]*3 for _a in range(2)] for _b in range(2)]
'''
[[[0, 0, 0], 
  [0, 0, 0]],
      ...   ]
'''

3)九宫格的问题在于对每个九宫格其行号和列号的特点

后记

在这三道题的题解中会出现dfs和回溯的两种解法,简要区别:
1)回溯是特殊的dfs,回溯的应用场景在于n叉树背景,dfs经常用于二叉树
2)两者都是要走到不能再走,所以递归方式是一致的,只是回溯是一定要做撤销处理,而dfs则视情况而定,所以也称回溯是特殊的dfs
3)回溯给的模板一般都是返回空,但也有返回bool等类型的情况,大多发生于题目保证一定有符合的路径,无需写终止条件,只要得到一条路径即返回

回溯专题刷完了!总结时间到!
记模板,构建树,确定每层和递归参数,确定终止条件,如何剪枝(重要的去重技巧)
文章讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值