十 动规
1.1 基础
动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
- 即 >>> 搜索的优化
动态规划(DP, dynamic programming)是一种对问题的状态空间进行分阶段、有顺序、不重复、 决策性遍历的算法
-
动态规划的关键与前提:
重叠子问题——与递归、分治等一样,要具有同类子问题,用若干维状态表示
最优子结构 ——状态对应着一个最优化目标,并且最优化目标之间具有推导关系
无后效性——问题的状态空间是一张有向无环图(可按一定的顺序遍历求解) -
动态规划一般采用递推的方式实现
也可以写成递归或搜索的形式,因为每个状态只遍历一次,也被称为记忆化搜索 -
动态规划三要素:阶段、状态、决策
1.2 题目
1.2.1 53 . 最大子数组和
- 思路: 𝑓[𝑖] 表示以 𝑖 为结尾的最大子序和
𝑓 [𝑖] = max (𝑓 [𝑖 − 1] + 𝑛𝑢𝑚𝑠 [𝑖] , 𝑛𝑢𝑚𝑠[𝑖])
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
f = [nums[0]]
for i in range(1, n):
f.append(max(nums[i], f[i - 1] + nums[i]))
return max(f)
1.2.2 1143 . 最长公共子序列
- 思路:只关心数量>>> 动规 >>> 观察蛮力搜索状态 >> 寻找变化信息 >>> 确定最优子结构, 边界
f[i][j]
表示text1
的前i
个字符和text2
的前j
个字符能组成的LCS的长度
如果 text1[i] = text2[j]
: f[i][j] = f[i - 1][j-1] + 1
如果 text1[i] != text2[j]
: f[i][j] = max(f[i - 1][j], f[i][j-1] )
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
f = [[0]*(n + 1) for _ in range(m + 1)]
text1 = " " + text1
text2 = " " + text2
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i] == text2[j]:
f[i][j] = f[i - 1][j - 1] + 1
else:
f[i][j] = max(f[i - 1][j], f[i][j - 1])
return f[-1][-1]
1.2.3 121 . 买卖股票的最佳时机
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp1 = 0
dp2 = 10e9
for price in prices:
dp2 = min(dp2, price)
dp1 = max(dp1, price - dp2)
return dp1
1.2.4 122 . 买卖股票的最佳时机 II
𝑓 [𝑖,𝑗]代表第 𝑖天结束时, 持有 𝑗股(0或1)
←表示max更新
- 买: 𝑓 [𝑖, 1] ← 𝑓 [𝑖 − 1,0] − 𝑝𝑟𝑖𝑐𝑒𝑠 [𝑖]
- 卖: 𝑓 [𝑖, 0] ← 𝑓 [𝑖 − 1,1] + 𝑝𝑟𝑖𝑐𝑒𝑠[𝑖]
- 不买不卖: 𝑓 [𝑖,𝑗] ← 𝑓 [𝑖 − 1,𝑗]
class Solution:
def maxProfit(self, prices: List[int]) -> int:
prices = [0] + prices
f = [[-1e9, -1e9] for _ in range(len(prices))]
f[0][0] = 0
for i in range(1, len(prices)):
f[i][1] = max(f[i][1], f[i - 1][0] - prices[i])
f[i][0] = max(f[i][0], f[i - 1][1] + prices[i])
for j in range(2):
f[i][j] = max(f[i][j], f[i-1][j])
return f[-1][0]
1.2.5 516 . 最长回文子序列
- 思路:
dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
s[i] = s[j]
时 >>
dp[i][j] = dp[i + 1][j - 1] + 2
;
s[i] != s[j]
时 >>
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0]*n for _ in range(n)]
for i in range(n-1, -1, -1):
dp[i][i] = 1
for j in range(i+1, n):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j-1])
return dp[0][-1]