leetcode刷题——动态规划

动规以空间换取时间

动态规划刷题总结以下类型及leetcode题目:

斐波那契数列

  • 70 爬楼梯(easy)
  • 198 打家劫舍(easy)
  • 213 打家劫舍 II(medium)

矩阵路径

  • 64 最小路径和(medium)
  • 62 不同路径(medium)

数组区间

  • 303 区域和检索 - 数组不可变(easy)
  • 413 等差数列划分(medium)

分割整数

  • 343 整数拆分(medium)
  • 279 完全平方数(medium)
  • 91 解码方法(medium)

最长递增子序列

  • 300 最长上升子序列(medium)
  • 646 最长数对链(medium)
  • 376 摆动序列(medium)

最长公共子序列

  • 1143 最长公共子序列(medium)

股票交易

  • 121 买卖股票的最佳时机(easy)
  • 122 买卖股票的最佳时机II(easy)
  • 123 买卖股票的最佳时机 III(hard)
  • 188 买卖股票的最佳时机 IV(hard)
  • 309 最佳买卖股票时机含冷冻期(medium)
  • 714 买卖股票的最佳时机含手续费(medium)

字符串编辑

  • 583 两个字符串的删除操作(medium)
  • 72 编辑距离(hard)
  • 650 只有两个键的键盘(medium)

斐波那契数列

思路: 以70题爬楼梯为例。
每次可以爬一阶或者二阶楼梯,上n阶楼梯一共有多少种方法。
1. 最后一次上1阶,之前的n-1阶楼梯的方法
2. 最后上2阶,之前n-2 阶楼梯的方法

因此总的上楼梯的方法为n-1阶头题的上法加上n-2阶楼梯的上法。
利用递归方法求解,找出其中的重叠子问题进行优化求解。

动态规划以空间换取时间优化

70 爬楼梯(easy)

https://leetcode-cn.com/problems/climbing-stairs/

  • 递归法(超出时间限制)
class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        if  n==2:
            return 2
        return self.climbStairs(n-1) + self.climbStairs(n-2)
  • 动规法: 在递归法解决方案中,存在大量的重复计算,例如在求f(5)时,需要计算f(4)与f(3),在求解f(4)时,依然求解一次f(3),
    当n的数目非常大的时候,存在更大量的重复计算。

因此为了解决这些重复计算,在计算的过程中,将值保存下来,利用空间换取时间。

class Solution:
    def climbStairs(self, n: int) -> int:
        memo = [-1 for _ in range(n)]
        memo[0] = 1
        if n >1:
            memo[1] = 2
        for i in range(2, n):
            memo[i] = memo[i-1] + memo[i-2]
        
        return memo[n-1]

198 打家劫舍(easy)

https://leetcode-cn.com/problems/house-robber/

  • 递归(超出时间限制)
class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums: return 0
        if len(nums) <= 2: return max(nums)
        return max(nums[0] + self.rob(nums[2:]), nums[1] + self.rob(nums[3:]))
  • 动规
class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums: return 0
        if len(nums)<2: return nums[0]
        memo = [0] * len(nums)
        memo[0] = nums[0]
        memo[1] = nums[1]
        for i in range(2, len(nums)):
            memo[i] = nums[i] + max(memo[:i-1])
        return max(memo[-1], memo[-2])

213 打家劫舍 II(medium)

https://leetcode-cn.com/problems/house-robber-ii/

  • 思路:这一题介绍的是一个环形的列表,可以依然采用上题的做法,分为两部分
    1. 第一部分,如果偷取第一间房屋,对于nums[:n-1]依然是上题的做法
    2. 第二部分,如果偷取最后一间房屋,对于nums[1:]依然是上题的做法
    3. 最后取两部分的最值
class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:return 0
        if len(nums)<=2:return max(nums)
        dp = [0] *  (len(nums)-1)
        # 偷取第一间房屋
        dp[0] = nums[0]
        dp[1] = nums[1]
        for i in range(2,len(nums)-1):
            dp[i] = nums[i] + max(dp[:i-1])
        res = max(dp[-1], dp[-2])
        # 偷取最后一间房屋
        dp[0] = nums[1]
        dp[1] = nums[2]
        for i in range(2,len(nums)-1):
            dp[i] = nums[i+1] + max(dp[:i-1])
        return max(res, dp[-1], dp[-2])

矩阵路径

64 矩阵的最小路径和

https://leetcode-cn.com/problems/minimum-path-sum/

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:

        if not grid: return 0
        m = len(grid)
        n = len(grid[0])
        dp = [[0 for _ in range(n)] for _ in range(m)]
        dp[0][0] = grid[0][0]
        # 最上边的一行
        for j in range(1, n):
            dp[0][j] = dp[0][j-1] + grid[0][j]
        # 最左边的一行
        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        return dp[-1][-1]

62 不同路径(medium)

https://leetcode-cn.com/problems/unique-paths/

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        if  m == 1 : return 1
        if n == 1 : return 1
        dp = [[0] * n] * m
        for i in range(m):
            dp[i][0] = 1
        for i in range(n):
            dp[0][i] = 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[-1][-1]

数组区间

303 区域和检索 - 数组不可变(easy)

https://leetcode-cn.com/problems/range-sum-query-immutable/

class NumArray:

    def __init__(self, nums: List[int]):
        self.dp = [0 for _ in range(len(nums))]
        if not nums: return 
        self.dp[0] = nums[0]
        for i in range(1, len(nums)):
            self.dp[i] = self.dp[i-1] + nums[i]
        # print(self.dp)
    def sumRange(self, i: int, j: int) -> int:
        if i ==0:
            return self.dp[j]
        else:
            return self.dp[j] - self.dp[i-1]


# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)

413 等差数列划分(medium)

https://leetcode-cn.com/problems/arithmetic-slices/

class Solution:
    def numberOfArithmeticSlices(self, A: List[int]) -> int:
        if not A: return 0
        if len(A) <3: return 0

        # dp表示以当前位置为结尾的等差数列的个数
        dp = [0] * len(A)
        dp[0] = 0
        for i in range(1, len(A)-1):
            if A[i+1] + A[i-1] == 2*A[i]:
                dp[i]  = dp[i-1] + 1
        return sum(dp)

分割整数

343 整数拆分(medium)

https://leetcode-cn.com/problems/integer-break/

  • 递归法:构建递归树进行遍历
class Solution:
    def integerBreak(self, n: int) -> int:
        #递归法
        if n <=2 : return 1
        res = -1
        for i in range(1, n):
            res = max(res, max(i*(n-i), i * self.integerBreak(n-i)))
        return res
  • 动规法:解决递归中的重叠子问题
class Solution:
    def integerBreak(self, n: int) -> int:

        dp = [-1] * (n+1)
        dp[0] = 1
        for i in range(1, n+1):
            for j in range(1, i):
                dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
        return dp[-1]

279 完全平方数(medium)

https://leetcode-cn.com/problems/perfect-squares/

class Solution:
    def numSquares(self, n: int) -> int:
        # 利用动规的方法, dp表示到i为止的最少完全平方数的个数
        dp = [0]*(n+1)
        for i in range(1,n+1):
            dp[i] = i
            j = 1
            while (i - j*j) >= 0:
                dp[i] = min(dp[i],dp[i-j*j]+1)
                j += 1
        return dp[-1]

91 解码方法(medium)

https://leetcode-cn.com/problems/decode-ways/

class Solution:
    def numDecodings(self, s: str) -> int:

        n = len(s)
        if n==0: return 0
        dp = [0] * (n+1)
        if s[0] == '0': return 0
        dp[0] = 1
        dp[1] = 1 if s[0]!='0' else 0 
        for i in range(1,n):
            if s[i]!='0':
                dp[i+1] += dp[i]
            if s[i-1:i+1]>='10' and s[i-1:i+1]<='26':
                dp[i+1] += dp[i-1]
        
        return dp[-1]

最长递增子序列

300 最长上升子序列(medium)

https://leetcode-cn.com/problems/longest-increasing-subsequence/

  • 递归回溯:超出时间限制
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        #暴力解法,递归回溯

        def helper(nums, root, res):
            if not nums:
                self.res.append(res) 
                return 

            for i in range(len(nums)):
                if nums[i] <=root: continue
                helper(nums[i+1:], nums[i], res+1)
            self.res.append(res)
            return

        if not nums: return 0
        if len(nums) == 1: return 1
        self.res = []
        for i in range(len(nums)):
            # print(self.res)
            helper(nums[i+1:], nums[i], 1)
        # print(self.res)
        return max(self.res) if self.res else 1
  • 动态规划
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:  return 0
        if len(nums) == 1: return 1
        # dp状态表示以第i个位结尾的最长的上升子序列
        dp = [1] * len(nums)
        for i in range(1, len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
        print(dp)
        return max(dp)

646 最长数对链(medium)

https://leetcode-cn.com/problems/maximum-length-of-pair-chain/

class Solution:
    def findLongestChain(self, pairs: List[List[int]]) -> int:

        if not pairs: return 0
        if len(pairs) == 1: return 1
        # 根据数对的第一个数进行排序
        pairs.sort()
        # 状态dp表示以第i个数对为结尾的最长的数对链
        dp = [1] * len(pairs)
        
        for i in range(len(pairs)):
            for j in range(i):
                if pairs[i][0] > pairs[j][1]:
                    dp[i] = max(dp[i], dp[j]+1)
        # print(dp)
        return max(dp)

376 摆动序列(medium)

https://leetcode-cn.com/problems/wiggle-subsequence/

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:

        if not nums: return 0
        dp_up = [1] * len(nums)
        dp_down = [1] * len(nums)

        for i in range(len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp_up[i] = max(dp_up[i], dp_down[j]+1)
                elif nums[i]<nums[j]:
                    dp_down[i] = max(dp_down[i], dp_up[j]+1)

        return max(max(dp_up), max(dp_down)) 

最长公共子序列

1143 最长公共子序列(medium)

https://leetcode-cn.com/problems/longest-common-subsequence/

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:

        if not text1 or not text2: return 0
        m = len(text1)
        n = len(text2)
        dp = [[0 for _ in range(n+1)] for _ in range(m+1)]

        for i in range(1, m+1):
            for j in range(1, n+1):
                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        # print(dp)
        return dp[-1][-1]

股票交易

base case:
#时间从第一天开始
dp[0][k][0] = 0
dp[0][k][1] = -prices[0]

# k为0表示不允许交易
dp[i][0][0] = 0
dp[i][0][1] = -infinity

状态转移方程:
状态方程中的 i 表示第i天, k表示剩余的操作次数, 0表示不持有股票,1表示持有股票
再第i天不持有股票, 那么其最大利润为上一天不持有股票与上一天持有股票卖掉二者的最大值
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
在第i天持有股票,那么其最大利润为上一天持有股票与上一天不持有股票,然后重新购买二者的最大值
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

121 买卖股票的最佳时机(easy)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices: return 0
        dp = [[0 for _ in range(2)] for _ in range(len(prices))]
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        for i in range(1, len(prices)):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
            dp[i][1] = max(dp[i-1][1],  -prices[i])
        # print(dp)
        return dp[-1][0]

122 买卖股票的最佳时机II(easy)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices: return 0
        if len(prices) == 1: return 0
        dp = [[0 for _ in range(2)]for _ in range(len(prices))]
        dp[0][0] = 0
        dp[0][1] = -prices[0]

        for i in range(1, len(prices)):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
        # print(dp)
        return dp[-1][0]

123 买卖股票的最佳时机 III(hard)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

class Solution:
    def maxProfit(self, prices: List[int]) -> int:

        if not prices: return 0
        max_k = 2
        dp = [[[0 for _ in range(2)] for _ in range(max_k+1)] for _  in range(len(prices)+1)]
        # i从第1天开始,0表示还没有开始, k从1开始,0表示不允许操作
        for i in range(1, len(prices)+1):
            for k in range(1, max_k+1):
                if i == 1:
                    dp[0][k][1] = float('-inf')
                    dp[0][k][0] = 0
                if k == 1:
                    dp[i][0][0] = 0
                    dp[i][0][1] = float('-inf')
                dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i-1])
                dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i-1])
        # print(dp)
        return dp[-1][-1][0]

188 买卖股票的最佳时机 IV(hard)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if not prices: return 0
        if k <= len(prices)//2:
            dp = [[[0 for _ in range(2)] for _ in range(k+1)] for _ in range(len(prices)+1)]
            for i in range(1, len(prices)+1):
                for j in range(1, k+1):
                    if i == 1:
                        dp[i-1][j][0] = 0
                        dp[i-1][j][1] = float('-inf')
                    if j == 1:
                        dp[i][j-1][0] = 0

                    dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1]+prices[i-1])
                    dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i-1])

            # print(dp)
            return dp[-1][-1][0]
        else:
            dp = [[0 for _ in range(2)] for _ in range(len(prices)+1)]
            for i in range(1, len(prices)+1):
                if i == 1:
                    dp[i-1][0] = 0
                    dp[i-1][1] = float('-inf')
                dp[i][0] = max(dp[i-1][0], dp[i-1][1] +prices[i-1])
                dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i-1])
            return dp[-1][0]

309 最佳买卖股票时机含冷冻期(medium)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

class Solution:
    def maxProfit(self, prices: List[int]) -> int:

        if not prices: return 0
        if len(prices)==1: return 0
        dp = [[0 for _ in range(2)] for _ in range(len(prices))]
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        dp[1][0] = max(0, prices[1]-prices[0])
        dp[1][1] = max(-prices[0], -prices[1])

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

714 买卖股票的最佳时机含手续费(medium)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:

        if not prices: return 0
        
        dp = [[0 for _ in range(2)] for _ in range(len(prices))]
        dp[0][0] = 0
        dp[0][1] = -prices[0]

        for i in range(1, len(prices)):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]-fee)
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] -prices[i])

        return dp[-1][0]

字符串编辑

583 两个字符串的删除操作(medium)

https://leetcode-cn.com/problems/delete-operation-for-two-strings/

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:

        # 求出lcs
        if len(word1) ==0 or len(word2)==0:
            return len(word1) if len(word1)!=0 else len(word2)
        dp = [[0 for _ in range(len(word2)+1)]for _ in range(len(word1)+1)]

        for  i in range(1, len(word1)+1):
            for j in range(1, len(word2)+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        # print(dp)
        return len(word1)+ len(word2) - 2*dp[-1][-1]

72 编辑距离(hard)

https://leetcode-cn.com/problems/edit-distance/

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n1 = len(word1)
        n2 = len(word2)
        dp = [[0 for _ in range(n2 + 1)] for _ in range(n1 + 1)]
        # 第一行
        for j in range(1, n2 + 1):
            dp[0][j] = dp[0][j-1] + 1

        # 第一列
        for i in range(1, n1 + 1):
            dp[i][0] = dp[i-1][0] + 1

        for i in range(1, n1 + 1):
            for j in range(1, n2 + 1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1
        #print(dp)      
        return dp[-1][-1]

650 只有两个键的键盘(medium)

https://leetcode-cn.com/problems/2-keys-keyboard/

class Solution:
    def minSteps(self, n: int) -> int:

        dp = [float('inf') for _ in range(n+1)]
        dp[1] = 0
        
        for i in range(2, n+1):
            for j in range(1, i):
                if i%j == 0:
                    dp[i] = min(dp[i], dp[j]+int(i/j))

        return dp[-1]

跳转至标签分类页

你可能感兴趣的:(leetcode刷题)