动态规划必刷 12 题
线性 DP
1. 爬楼梯(LeetCode 70)
def climb_stairs(n):
if n <= 2:
return n
a, b = 1, 2
for _ in range(3, n + 1):
a, b = b, a + b
return b
2. 打家劫舍(LeetCode 198)
def rob(nums):
prev2, prev1 = 0, 0
for num in nums:
curr = max(prev1, prev2 + num)
prev2, prev1 = prev1, curr
return prev1
3. 最大子数组和(LeetCode 53)
def max_sub_array(nums):
curr_max = global_max = nums[0]
for num in nums[1:]:
curr_max = max(num, curr_max + num)
global_max = max(global_max, curr_max)
return global_max
4. 最长递增子序列(LeetCode 300)— O(n log n)
import bisect
def length_of_lis(nums):
tails = []
for num in nums:
idx = bisect.bisect_left(tails, num)
if idx == len(tails):
tails.append(num)
else:
tails[idx] = num
return len(tails)
状态机 DP — 股票问题
5. 买卖股票的最佳时机(LeetCode 121)
def max_profit(prices):
min_price = float('inf')
max_profit = 0
for price in prices:
min_price = min(min_price, price)
max_profit = max(max_profit, price - min_price)
return max_profit
6. 买卖股票的最佳时机含冷冻期(LeetCode 309)
def max_profit_with_cooldown(prices):
if not prices:
return 0
n = len(prices)
hold = [0] * n
sold = [0] * n
rest = [0] * n
hold[0] = -prices[0]
for i in range(1, n):
hold[i] = max(hold[i - 1], rest[i - 1] - prices[i])
sold[i] = hold[i - 1] + prices[i]
rest[i] = max(rest[i - 1], sold[i - 1])
return max(sold[-1], rest[-1])
7. 买卖股票的最佳时机 III(LeetCode 123)— 最多两次
def max_profit_3(prices):
buy1 = buy2 = float('-inf')
sell1 = sell2 = 0
for price in prices:
buy1 = max(buy1, -price)
sell1 = max(sell1, buy1 + price)
buy2 = max(buy2, sell1 - price)
sell2 = max(sell2, buy2 + price)
return sell2
背包问题
8. 分割等和子集(LeetCode 416)— 01背包
def can_partition(nums):
total = sum(nums)
if total % 2 != 0:
return False
target = total // 2
dp = [False] * (target + 1)
dp[0] = True
for num in nums:
for j in range(target, num - 1, -1):
dp[j] = dp[j] or dp[j - num]
return dp[target]
9. 零钱兑换(LeetCode 322)— 完全背包
def coin_change(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
区间 DP
10. 最长回文子序列(LeetCode 516)
def longest_palindrome_subseq(s):
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][n - 1]
二维 DP
11. 不同路径(LeetCode 62)
def unique_paths(m, n):
dp = [1] * n
for _ in range(1, m):
for j in range(1, n):
dp[j] += dp[j - 1]
return dp[-1]
12. 编辑距离(LeetCode 72)
def min_distance(word1, word2):
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 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[m][n]
DP 类型速查
| 类型 | 特征 | 例题 |
|---|---|---|
| 线性 DP | 一维状态转移 | 爬楼梯、打家劫舍 |
| 状态机 DP | 多种状态互转 | 股票系列 |
| 01 背包 | 物品只能选一次 | 分割等和子集 |
| 完全背包 | 物品可重复选 | 零钱兑换 |
| 区间 DP | 区间两端递推 | 最长回文子序列 |
| 二维 DP | 网格路径类 | 不同路径、编辑距离 |
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。