【LeetCode】动态规划专题(持续更新......)

目录

注意:

53. 最大子序和

120. 三角形最小路径和

63. 不同路径 II

91. 解码方法

198. 打家劫舍

300. 最长上升子序列

72. 编辑距离

518. 零钱兑换 II


注意:

和b站up主大雪菜一起刷的https://www.bilibili.com/video/BV15441117yb

【LeetCode】动态规划专题(持续更新......)_第1张图片

53. 最大子序和

思路:

  • 状态表示dp[i]:以i结尾的最大子段和
  • 状态转移:dp[i] = max(dp[i-1],0)+nums[i],优化后,因为状态转移只与前一个dp[i-1]有关,可以直接用last代替
  • 初始化:dp[0]=nums[0]
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        res = - float('INF')
        last = 0
        for num in nums:
            now = max(last, 0) + num
            last = now
            res = max(res, now)
        return res

120. 三角形最小路径和

思路:

  • dp[i][j]:表示第i行第j列最小路径和
  • dp[i][j]=min(dp[i][j],dp[i-1][j]+nums[i][j])(从右上角下来)(j
  • dp[i][j] = min(dp[i][j], dp[i-1][j-1]+nums[i][j])(从左上角下来)(j>0不是最左边一列)
  • dp[0][0] = nums[0][0]
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        rows = len(triangle)
        cols = len(triangle)
        dp = [[float('INF')]*cols for _ in range(rows)]
        dp[0][0] = triangle[0][0]
        if rows==1:
            return dp[0][0]
        for i in range(1, rows):
            for j in range(i+1):
                if j > 0:
                    dp[i][j] = min(dp[i][j], dp[i-1][j-1] + triangle[i][j])
                if j < i:
                    dp[i][j] = min(dp[i][j], dp[i-1][j] + triangle[i][j])
        res = float('INF')
        for i in range(cols):
            res = min(res, dp[rows-1][i])
        return res

63. 不同路径 II

思路:

  • dp[i][j]:走到(i,j)有多多少种不同路径
  • dp[i][j] += dp[i-1][j](最后一步是向下)i>0
  • dp[i][j] += dp[i][j-1](最后一步是向右)j>0
  • 边界值,dp[0][0]=1
class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        rows,cols = len(obstacleGrid), len(obstacleGrid[0])
        dp = [[0]*cols for _ in range(rows)]
        for i in range(rows):
            for j in range(cols):
                # 遇到障碍
                if obstacleGrid[i][j]==1:
                    continue
                # 左上角
                if not i and not j:
                    dp[i][j] = 1
                if i>0:
                    dp[i][j] += dp[i-1][j]
                if j>0:
                    dp[i][j] += dp[i][j-1]
        return dp[rows-1][cols-1]

91. 解码方法

思路:

  • dp[i]:第i位有几种解码方式
  • dp[i]+=dp[i-1](最后一位解码,s[i]可以解码)
  • dp[i]+=dp[i-2](最后两位解码,s[i-1]s[i]可以解码)
  • 边界dp[0]=1(从dp[1]开始统计)
class Solution:
    def numDecodings(self, s: str) -> int:
        n = len(s)
        dp = [0]*(n+1)
        dp[0] = 1
        for i in range(1, n+1):
            # 最后一位可以表示
            if s[i-1]!='0':
                dp[i] += dp[i-1]
            if i>=2:
                sum_ = (ord(s[i-2])-ord('0'))*10 + (ord(s[i-1])-ord('0'))
                # 最后两位可以表示
                if 10<=sum_<=26:
                    dp[i] += dp[i-2]
        return dp[n]

198. 打家劫舍

思路:

  • 简单化整个问题,就是从一个数组里面挑出几个数,使他们的和为最大。
  • 限制条件是取得数不能是连续的
  • f[i]:以i为结尾的最大和,且nums[i]不取
  • g[i]:以i为结尾的最大和,且nums[i]取
  • f[i] = max(f[i-1],g[i-1])
  • g[i] = f[i-1]+nums[i]
class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        f, g = [0]*(n+1),[0]*(n+1)
        for i in range(1, n+1):
            f[i] = max(f[i-1], g[i-1])
            g[i] = f[i-1]+nums[i-1]
        return max(f[n], g[n])

300. 最长上升子序列

思路:

  • dp[i]:以nums[i]结尾的子序列的最长长度
  • dp[i] = max(dp[i], dp[j] + 1)
  • res存储最大长度
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        n = len(nums)
        dp = [1]*n
        res = 1
        for i in range(n):
            for j in range(i):
                if nums[j]

72. 编辑距离

思路:

  • dp[i][j]:word1第i个位置和word2第j个位置匹配的最少操作数
  • 插入:dp[i][j]=dp[i][j-1]+1,说明word1[0...i]和word[0....j-1]都已经匹配上了,现在需要插入一个
  • 删除:dp[i][j]=dp[i-1][j]+1,说明word1[0...i-1]和word[0....j]都已经匹配上了,现在需要删除一个
  • 替换:dp[i][j]=dp[i-1][j-1]+1
  • 不做操作:dp[i][j]=dp[i-1][j-1]
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n = len(word1)
        m = len(word2)
        dp = [[0]*(m+1) for _ in range(n+1)]
        # 边界 word1匹配上word2第0个位置也就是空 需要进行i次删除
        for i in range(n+1):
            dp[i][0] = i
        for i in range(m+1):
            dp[0][i] = i
        for i in range(1, n+1):
            for j in range(1, m+1):
                # 插入 删除
                dp[i][j] = min(dp[i-1][j], dp[i][j-1])+1
                dp[i][j] = min(dp[i][j], dp[i-1][j-1]+(word1[i-1]!=word2[j-1]))
        return dp[n][m]

518. 零钱兑换 II

思路:

  • 完全背包问题
  • dp[i][j]:前i种硬币凑到j的方案数
  • dp[i-1][j](不加第i种硬币的方案数)
  • dp[i-1][j-c]...dp[i-1][j-kc](用1....k个第i种硬币方案数)
  • dp[i][j]=dp[i-1][j]+dp[i-1][j-c]+dp[i-1][j-2c]+...+dp[i-1][j-kc]=dp[i-1][j]+dp[i][j-c]
  • 边界dp[i][0]=1,一种方案什么也不选
class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        n = len(coins)
        dp = [[0]*(amount+1) for _ in range(n+1)]
        dp[0][0]=1
        for i in range(1, n+1):
            for j in range(amount+1):
                dp[i][j] = dp[i-1][j]
                if j >= coins[i-1]:
                    dp[i][j] += dp[i][j-coins[i-1]]
        return dp[n][amount]

 

你可能感兴趣的:(学习,算法练习)