【leetcode82-91动态规划,91-95多维动态规划】

动态规划【82-91】

动态规划5步曲

  1. 确定dp数组 & 下标含义
  2. 递推公式
  3. dp数组初始化 【涉及乘法,dp[0] =1 】
  4. 遍历顺序
  5. 举例推导dp数组

背包问题

  1. 01背包(每件物品只能用0或1次):n件物品,最多装重量w的背包,第i件物品的重量为weight[i],价值value[i],每件物品只能装1次,求背包价值最大总和
  • 二维dp, dp[i][j]:从下标[0,i]任取,放进容量为j的背包,价值最大为dp[i][j]
    dp[i][j] = max( dp[i-1][j] , dp[i-1][j-weight[i]]+ value[i]) 拿不拿第i个物品
  • 一维dp, 先遍历物品i, 在倒序遍历容量j【倒序是为了保证:物品只拿一次】
    dp[j] = max(dp[j], dp[j-weight[i]] + value[i])

  1. 完全背包(每个物品数量无限)
    先遍历物品i,或者先正序遍历物品j都行
    【组合数:外层for物品,内层for容量】【排列数:外层for容量,内层for物品】

打家劫舍 dp[i]:下标i内(包括i)的房子,最多偷的金额为dp[i]

  1. 2个相邻都被偷,会报警 dp[i] = max(dp[i-1], dp[i-2]+nums[i]) 其实就是nums[i]偷不偷
  2. 房屋成环 res1 = rob(nums[:-1]) res2 = rob(nums[1:]) res = max(res1, res2)
  3. 房屋树形 dp[0]代表不偷该节点,得到的最大钱, dp[1]代表偷该节点,得到的最大钱

股票买卖的最佳时机dp[i][0]第i天持有股票,所获最大钱 dp[i][1]第i天不持有股票,所获最大钱

  1. 只能买卖1次 dp[i][0] = max(d[i-1][0], -price[i]) dp[i][1] = max(dp[i-1][0]. dp[i-1][0]+price[i])
  2. 多次买卖 dp[i][0] = max(d[i-1][0], -price[i] + dp[i-1][1])
  3. 只能买卖2次
  4. 只能买卖k次 dp[i][j]【 j=0不操作, j= 2k-1第k次买入,j=2k第k次卖出 】
  5. 含有冷冻期 dp[i][j]【 j=0持有股票, j=1不持有股票也不在冷冻期,也不是刚卖,j=2冷冻期,j=3刚卖出 】
  6. 卖出有手续费 dp[i][1] = max(dp[i-1][0]. dp[i-1][0]+price[i]-fee)

子序列问题

  1. 在一个序列里面:求最长递增子序列长度【 dp[i]:子序列答案以nums[i]结尾的最长递增子序列的长度】
    1.1 不连续 for i,遍历j<i, 如果nums[i] > nums[j],dp[i] = max(dp[i], dp[j]+1)
    1.2 连续 if num[i[ > nums[i-1]:dp[i] = dp[i-1]+1
    【求连续数组的最大和】 dp[i] = max(dp[i-1]+nums[i], nums[i])
  2. 两个序列:求最长公共子序列的长度【 dp[i][j]:A[0,i-1]和B[0,j-1]最长连续公共子序列的长度】
    2.1 不连续 if A[i-1]== B[j-1]:dp[i][j] = dp[i-1][j-1]+1 else:dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    2.2 连续 if A[i-1]== B[j-1]:dp[i][j] = dp[i-1][j-1]+1

回文子串

70.爬楼梯

在这里插入图片描述

'''
# dp[i]:爬到第i个楼梯,有dp[i]种办法
# dp[i] = dp[i-1] + dp[i-2]
# dp[1] = 1 dp[2] = 2
'''
class Solution:
    def climbStairs(self, n: int) -> int:
        if n<=2 : 
            return n

        dp = [1] * (n+1)
        dp[2] = 2
        for i in range(3, n+1):
            dp[i] = dp[i-1] + dp[i-2]

        return dp[n]

118.杨辉三角

在这里插入图片描述

在这里插入图片描述

'''
1.dp[i]:杨辉三角第i行
2.dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
3.dp每一排的第一个数和最后一个数都是 1,即 dp[i][0]=dp[i][i]=1
4.从上到下,从左到右
'''
class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        dp = [[1]*(i+1)  for i in range(numRows)]
        for i in range(2, numRows):
            for j in range(1, i):
                dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
        return dp

198.打家劫舍

在这里插入图片描述

'''
    1.dp[i]:到下标i户,偷最大金额
    2.dp[i] = max(dp[i-1], dp[i-2]+num[i-1])  #第i家偷不偷,偷的话:dp[i-2]+num[i-1],不偷的话:dp[i-1]
    3.dp[0]= nums[0] dp[1]= max(nums[0],nums[1])
    4.i从左到右
'''
class Solution:
    def rob(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return nums[0]

        dp = [0] * len(nums)
        dp[0] = nums[0]
        dp[1] = max(nums[0],nums[1])
        for i in range(2, len(nums)):
            dp[i] = max(dp[i-1], dp[i-2]+nums[i])
        return dp[len(nums)-1]

279.完全平方数,最少完全平方数

在这里插入图片描述

完全背包问题

'''
1.dp[i] : 和为i,完全平方数的最小数量
2. 变量j【从小到大】 dp[i] = min(dp[i-j**2]+1,dp[i])
3.dp[0] = 0,其他要初始化为最大值
'''
class Solution:
    def numSquares(self, n: int) -> int:
        dp = [float('inf')] * (n+1)
        dp[0] = 0
        for i in range(1, n+1):
            for j in range(1, int(i**(1/2))+1):
                dp[i] = min(dp[i-j**2]+1, dp[i])
        return dp[n]

322.零钱兑换,最少硬币数

在这里插入图片描述

完全背包

'''
1.dp[i] : 和为i,凑成i所需的 最少的硬币个数 
2. 遍历j,  dp[i] = min(dp[i-coins[j]]+1,dp[i])
3.dp[0] = 0,其他要初始化为最大值
'''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount+1)
        dp[0] = 0
      #  dp[j]:凑成总金额j所需的最少的硬币个数
        for i in coins:
            for j in range(1, amount+1):
                if j>=i:
                    dp[j] = min(dp[j-i]+1, dp[j])
        if dp[amount] == float('inf'):
            return -1
        return dp[-1]

139.单词拆分

在这里插入图片描述

完全背包

'''
1.dp[i]:   s[0:i]能不能被worddict拼接
2.dp[j] == True and s[j:i] in wordset:   dp[j] = True break
3.dp[0]=True ,其他全部为False
'''
class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        wordset = set(wordDict)
        n = len(s)
        dp = [False] * (n+1)
        dp[0] = True

        for i in range(1, n+1):
            for j in range(i):
                if dp[j] == True and s[j:i] in wordset:
                    dp[i] = True
                    break 
        return dp[n]

300.最长递增子序列

在这里插入图片描述

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)
        dp = [1] * len(nums) #下标为0-i的最长严格子序列长度
        dp[0] = 1
        res = 1
        for i in range(1, len(nums)):
            for j in range(0, i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j]+1)
            res = max(res, dp[i])
        return res

152.乘积最大子数组

在这里插入图片描述

我们只要记录前 i 的最小值,和最大值,那么 dp[i] = max(nums[i] * pre_max, nums[i] * pre_min, nums[i]),这里 0 不需要单独考虑,因为当相乘不管最大值和最小值,都会置 0
什么时候不连续:当max,min取到i,代表新的开始

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        if not nums:
            return
        res = nums[0]
        pre_max = nums[0]
        pre_min = nums[0]
        for i in nums[1:]:
            cur_max = max(pre_max*i, pre_min*i, i)
            cur_min = min(pre_max*i, pre_min*i, i)
            res = max(res, cur_max)
            pre_max = cur_max
            pre_min = cur_min
        return res

416.分割等和子集

在这里插入图片描述

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        '''
        就是看dp[sum/2]== sum//2,01背包
        1. dp[i]: 容量为i的背包,最多装载
        2. dp[i] = max(dp[i-num]+num, dp[i])
        3. dp[0] = 0
        '''
       
        sums = sum(nums)
        dp = [0] * (sums//2+1)
        if sums%2 == 1:
            return False
        for i in nums:
            for j in range(sums//2, i-1, -1):
                dp[j] = max(dp[j], dp[j-i]+i)
        return dp[sums//2] == sums//2

32.最长有效括号

在这里插入图片描述

如果成对,栈就pop

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        stack=[] # 构建一个栈记录字符index
        ans=0
        for i in range(len(s)):
            # 如果栈非空,且当前为右括号,且有记录的左括号,则出栈
            if stack and s[i]==")" and s[stack[-1]]=="(":
                stack.pop()
                # 如果出栈后变成空栈,则说明整个[0:i]的区间都是合格的,长度为i+1
                # 如果出栈后非空,则说明区间[stack[-1]:i]是合格的
                ans=max(ans,i-(stack[-1] if stack else -1))
            else:
                # 以下3个条件会触发else
                # 如果是空栈
                # 或者当前字符为左括号(需要寻找匹配的右括号)
                # 或者当前字符为右括号,而且栈顶记录的也是右括号(不合格的情况,永远不会被pop)
                stack.append(i)
                
        return ans

多维动态规划【91-95】

62.不同路径

在这里插入图片描述

'''
1. dp[i][j]:到达下标{i,j},总共的路径
2.dp[i][j] = dp[i-1][j] + dp[i][j-1]
3. dp[0][i] = d[0][j] = 1
'''
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[0] * n for _ in range(m)]
        
        for i in range(m):
            dp[i][0] = 1
        for j in range(n):
            dp[0][j] = 1
        
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        
        return dp[m - 1][n - 1]

64.最小路径和

在这里插入图片描述

'''
     1. dp[i][j]:到下标 (i,j),数字总和最小
     2. dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j]
     3. dp[0][0] = grid[0][0]
     dp[i][0] = sum(grid[i][0]) dp[0][j] = sum(grid[0][j])
'''
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:

        m = len(grid)
        n = len(grid[0])
        dp = [[0]*n for _ in range(m)]
        dp[0][0] = grid[0][0]
        for i in range(1,m):
            dp[i][0] = grid[i][0] + dp[i-1][0]
        for j in range(1,n):
            dp[0][j] = grid[0][j] + dp[0][j-1]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j]
        return dp[-1][-1]
        

5.最长回文子串

在这里插入图片描述

在这里插入图片描述

'''
1.dp[i][j]:区间[i:j]左闭右闭的子串,是否是回文子串
2.如果s[i] != s[j], dp[i][j] = False
  如果s[i]== s[j]【1.下标相同,是回文子串 ; 
                   2.下标相差为1,‘aa’,也是回文; 
                    3.下标相差大于1,如果dp[i+1][j-1]也是回文,才是回文】 
3.初始化:dp[i][j] = False
4.i从大到小, j从小到大
'''
class Solution:
    def longestPalindrome(self, s: str) -> str:
        dp = [[False] * len(s) for _ in range(len(s))]
        maxlenth = 0
        left = 0
        right = 0
        for i in range(len(s) - 1, -1, -1):
            for j in range(i, len(s)):
                if s[j] == s[i]:
                    if j - i <= 1 or dp[i + 1][j - 1] == True:
                        dp[i][j] = True
                if dp[i][j] and j - i + 1 > maxlenth:
                    maxlenth = j - i + 1
                    left = i
                    right = j
        return s[left:right + 1]

1143.最长公共子序列

在这里插入图片描述

'''
1. dp[i][j]:以text1[i-1]结尾,text2[j-1]结尾,最长公共序列的长度
2. 如果text2[i-1] == text1[j-1],  dp[i][j] = dp[i-1][j-1] + 1
    如果text2[i-1] != text1[j-1],  dp[i][j]=max(dp[i-1][j], dp[i][j-1])
3.dp[i][0]; dp[0][j]无意义

'''
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        if len(text1) == 0 or len(text2) == 0: 
            return 0
            
        dp = [[0] * (len(text1)+1) for _ in range(len(text2)+1)]
       
        for i in range(1, len(text2)+1):
            for j in range(1, len(text1)+1):
                if text2[i-1] == text1[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j]=max(dp[i-1][j], dp[i][j-1])
        return dp[-1][-1]

72.编辑距离

在这里插入图片描述

'''
1. dp[i][j] : 将word1[0:i]转变成word2[0:j],最少操作数
2. 如果 word1[i-1]==word2[j-1]: dp[i][j] = dp[i-1][j-1]
    如果  word[i-1]!=word2[j-1]: dp[i][j] = min(dp[i-1][j], dp[i][j-1],dp[i-1][j-1])+1
3.dp[0][j]=j dp[i][0] = i
'''
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp = [[0] * (len(word2)+1) for _ in range(len(word1)+1) ]
        for i in range(len(word2)+1):
            dp[0][i] = i
        for i in range(len(word1)+1):
            dp[i][0] = i
        
        for i in range(1, len(word1)+1):
            for j in range(1, len(word2)+1):
                if word2[j-1]==word1[i-1]: 
                    dp[i][j] = dp[i-1][j-1] 
                else:
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1],dp[i-1][j-1])+1
        return dp[len(word1)][len(word2)]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值