动规以空间换取时间
动态规划刷题总结以下类型及leetcode题目:
思路: 以70题爬楼梯为例。
每次可以爬一阶或者二阶楼梯,上n阶楼梯一共有多少种方法。
1. 最后一次上1阶,之前的n-1阶楼梯的方法
2. 最后上2阶,之前n-2 阶楼梯的方法
因此总的上楼梯的方法为n-1阶头题的上法加上n-2阶楼梯的上法。
利用递归方法求解,找出其中的重叠子问题进行优化求解。
动态规划以空间换取时间优化
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)
因此为了解决这些重复计算,在计算的过程中,将值保存下来,利用空间换取时间。
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]
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])
https://leetcode-cn.com/problems/house-robber-ii/
nums[:n-1]
依然是上题的做法nums[1:]
依然是上题的做法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])
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]
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]
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)
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)
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]
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]
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]
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)
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)
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))
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])
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]
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]
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]
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]
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]
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]
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]
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]
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]
跳转至标签分类页