[LeetCode108双周赛&LeetCode353周赛] 学习用记忆化搜索解决 DP 问题

参考灵神直播和代码

补充:DP 中常见的记忆化搜索 @cache 装饰器

@cache 装饰器的作用:将传入不同参数得到的函数值存储到缓存,避免下次传递相同参数重复计算结果,可用于解决递归函数重复计算问题,比如递归求斐波那契问题。

6899. 达到末尾下标所需的最大跳跃次数

https://leetcode.cn/problems/maximum-number-of-jumps-to-reach-the-last-index/

Solution

记忆化搜索

dfs(i) 表示以 nums[i] 为终点求的最大跳跃数

class Solution:
    def maximumJumps(self, nums: List[int], target: int) -> int:
        n = len(nums)
        @cache
        def dfs(i :int) -> int:
            if i == 0:
                return 0
            res = -inf
            for j in range(i):
                if abs(nums[i] - nums[j]) <= target:
                    res = max(res, dfs(j) + 1)
            return res
        ans = dfs(n-1)
        return ans if ans > 0 else -1

递推(逆向)

dp(i) 表示以 nums[i] 为终点求的最大跳跃数

class Solution:
    def maximumJumps(self, nums: List[int], target: int) -> int:
        n = len(nums)
        dp = [-inf] * n
        dp[0] = 0
        for i in range(1, n):
            for j in range(i):
                if abs(nums[i] - nums[j]) <= target:
                    dp[i] = max(dp[i], dp[j] + 1)
        return dp[-1] if dp[-1] > 0 else -1

递推(正向)

class Solution:
    def maximumJumps(self, nums: List[int], target: int) -> int:
        n = len(nums)   
        dp = [-1] * n
        dp[0] = 0
        for i in range(n):
            if dp[i] >= 0: # 关键判断
                for j in range(i + 1, n):
                    if abs(nums[j] - nums[i]) <= target:
                        dp[j] = max(dp[j], dp[i] + 1)
        return dp[-1]

6912. 构造最长非递减子数组

https://leetcode.cn/problems/longest-non-decreasing-subarray-from-two-arrays/

Solution

记忆化搜索

dfs(i, 0/1) 表示以 nums1[i] 或者 nums2[i] 结尾的最大

class Solution:
    def maxNonDecreasingLength(self, nums1: List[int], nums2: List[int]) -> int:
        nums = (nums1, nums2)
        @cache
        def dfs(i: int, j: int) -> int:
            if i == 0:
                return 1
            res = 1
            # 如果 nums1[i-1] 符合条件
            if nums1[i-1] <= nums[j][i]:
                res = max(res, dfs(i - 1, 0) + 1)
            # 如果 nums2[i-1] 符合条件
            if nums2[i-1] <= nums[j][i]:
                res = max(res, dfs(i - 1, 1) + 1)
            return res
        ans = 0
        for i in range(len(nums1)):
            ans = max(ans, dfs(i, 0), dfs(i, 1))
        return ans

6919. 使数组中的所有元素都等于零

https://leetcode.cn/problems/apply-operations-to-make-all-array-elements-equal-to-zero/

Solution

差分数组

差分数组求前缀和即为各个区间的最终变化结果,差分数组可以把一个区间操作变为对两个数的操作,从而节省时间。

class Solution:
    def checkArray(self, nums: List[int], k: int) -> bool:
        n = len(nums)
        d = [0] * (n + 1) # 差分数组
        pre = 0 # 记录差分数组的前缀和, 边扫描, 边计算

        for i, x in enumerate(nums):
            pre += d[i]
            x += pre
            if x == 0:
                continue
            # x < 0 表示已经减到了负数, 不符合要求
            # i + k > 0 表示越界了, 不符合要求
            if x < 0 or i + k > n:
                return False
            # 差分过程
            pre -= x
            d[i+k] += x
        return True

6923. 将字符串分割为最少的美丽子字符串

https://leetcode.cn/problems/partition-string-into-minimum-beautiful-substrings/description/

相关题目

131. 分割回文串

Solution

记忆化搜索

dfs(i) 表示从 s[i]s[n-1] 最少分割成多少段

class Solution:
    def minimumBeautifulSubstrings(self, s: str) -> int:
        pow5 = [bin(5**i)[2:] for i in range(10) if len(bin(5**i)[2:]) <= 15]
        n = len(s)
        @cache
        def dfs(i :int) -> int:
            if i == n:
                return 0
            if s[i] == '0': # 表示不合法的状态
                return inf 

            res = inf

            # 遍历 5 的幂二进制串, 看选哪一个
            for t in pow5:
                if i + len(t) > n:
                    break
                if s[i:i + len(t)] == t:
                    res = min(res, dfs(i + len(t)) + 1)
            return res
        ans = dfs(0)
        return ans if ans < inf else -1


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哇咔咔负负得正

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值