动态规划篇——python.刷题记录

70. 爬楼梯

方法一:递归 + 记录返回值 = 记忆化搜索

注意到「先爬 1 个台阶,再爬 2 个台阶」和「先爬 2 个台阶,再爬 1 个台阶」,都相当于爬 3 个台阶,都会从 dfs(i) 递归到 dfs(i−3)。

一叶知秋,整个递归中有大量重复递归调用(递归入参相同)。由于递归函数没有副作用,同样的入参无论计算多少次,算出来的结果都是一样的,因此可以用记忆化搜索来优化:

  • 如果一个状态(递归入参)是第一次遇到,那么可以在返回前,把状态及其结果记到一个 memo 数组中。
  • 如果一个状态不是第一次遇到(memo 中保存的结果不等于 memo 的初始值),那么可以直接返回 memo 中保存的结果。

注意:memo 数组的初始值一定不能等于要记忆化的值!例如初始值设置为 0,并且要记忆化的 dfs(i) 也等于 0,那就没法判断 0 到底表示第一次遇到这个状态,还是表示之前遇到过了,从而导致记忆化失效。一般把初始值设置为 −1。本题由于方案数均为正数,所以可以初始化成 0。

class Solution:
    def climbStairs(self, n: int) -> int:
        @cache  # 缓存装饰器,避免重复计算 dfs 的结果
        def dfs(i: int) -> int:
            if i <= 1:  # 递归边界
                return 1
            return dfs(i - 1) + dfs(i - 2)
        return dfs(n)

时间复杂度:O(n)。由于每个状态只会计算一次,动态规划的时间复杂度 = 状态个数 × 单个状态的计算时间。本题状态个数等于 O(n),单个状态的计算时间为 O(1),所以动态规划的时间复杂度为 O(n)。
空间复杂度:O(n)。有多少个状态,memo 数组的大小就是多少。

118. 杨辉三角

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        c = [[1] * (i+1) for i in range(numRows)]
        for i in range(2,numRows):
            for j in range(1,i):
                c[i][j] = c[i-1][j]+c[i-1][j-1]
        return c

198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

这个问题的核心思想是动态规划。我们可以将问题分解为子问题:

  • 对于每个房子 i,我们有两种选择:偷或不偷。
  • 如果我们偷了第 i 个房子,那么我们不能偷第 i-1 个房子,所以最大金额是 dfs(i - 2) + nums[i]
  • 如果我们不偷第 i 个房子,那么最大金额是 dfs(i - 1)
  • 我们取这两种选择中的最大值作为 dfs(i) 的结果。
class Solution:
    def rob(self, nums: List[int]) -> int:
        @cache  # 缓存装饰器,避免重复计算 dfs 的结果
        def dfs(i: int) -> int:  # dfs(i) 表示从 nums[0] 到 nums[i] 最多能偷多少
            if i < 0:  # 递归边界(没有房子)
                return 0
            return max(dfs(i - 1), dfs(i - 2) + nums[i])
        return dfs(len(nums) - 1)  # 从最后一个房子开始思考
  • 时间复杂度:O(n)。其中 n 为 nums 的长度。
  • 空间复杂度:O(n)。

空间优化

  • 时间复杂度:O(n)。其中 n 为 nums 的长度。
  • 空间复杂度:O(1)。仅用到若干额外变量。
class Solution:
    def rob(self, nums: List[int]) -> int:
        f0 = f1 = 0
        for x in nums:
            f0, f1 = f1, max(f1, f0 + x)
        return f1

你可能感兴趣的:(算法刷题记录,我的学习记录,动态规划,算法)