目录
注意:
和b站up主大雪菜一起刷的https://www.bilibili.com/video/BV15441117yb
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<i不是最右边一列)
- 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]<nums[i]:
dp[i] = max(dp[i], dp[j]+1)
res = max(res, dp[i])
return res
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]