动态规划经典问题
动态规划是一种常用的算法设计方法,它通过将复杂的问题分解为多个子问题来解决,并利用这些子问题的解构建原问题的最优解。这种方法尤其适用于具有重叠子问题和最优子结构性质的问题。
01 最长公共子序列(Longest Common Subsequence, LCS)
给定两个字符串 X 和 Y,寻找它们的最长公共子序列是动态规划的一个经典应用。一个子序列是在原字符串中保持字符顺序但不必要连续出现的序列。
解决思路
- 定义状态:设
LCS(X[0..m], Y[0..n]) = result。 - 递推关系式:
- 如果
X[m] == Y[n],那么result = LCS(X[0..m-1], Y[0..n-1]) + X[m] - 否则,
result = max(LCS(X[0..m-1], Y[0..n]), LCS(X[0..m], Y[0..n-1]))
- 如果
Python实现
def longest_common_subsequence(X, Y):
m = len(X)
n = len(Y)
# 创建一个 (m+1) x (n+1) 的二维数组来存储子问题的解
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 填充dp数组
for i in range(1, m + 1):
for j in range(1, n + 1):
if X[i - 1] == Y[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
# 返回结果
return dp[m][n]
02 打家劫舍(House Robber)
给定一个非空整数数组,代表每栋房子的金钱数目。在每个晚上,你可以任意选择一所房屋进行抢劫,但是如果你连续抢劫了两所相邻的房子,则会触发报警系统。你的任务是计算最多可以偷窃到多少金钱。
解决思路
- 定义状态:设
dp[i]表示前i个房子中能获得的最大金额。 - 递推关系式:
- 不选择当前房屋:
dp[i] = dp[i-1] - 选择当前房屋:
dp[i] = house_value[i] + dp[i-2]
- 不选择当前房屋:
Python实现
def rob(houses):
n = len(houses)
if n == 0:
return 0
# 如果只有1个房子,直接偷窃它
if n == 1:
return houses[0]
# 初始化前两个状态
dp = [0] * n
dp[0], dp[1] = houses[0], max(houses[0], houses[1])
# 填充dp数组
for i in range(2, n):
dp[i] = max(dp[i-1], dp[i-2] + houses[i])
# 返回结果
return dp[-1]
03 最小编辑距离(Edit Distance)
给定两个单词 word1 和 word2,找出将 word1 转换为 word2 所需的最少操作次数。允许的操作有:插入一个字符、删除一个字符或替换一个字符。
解决思路
- 定义状态:设
dp[i][j]代表将word1[0..i-1]转换为word2[0..j-1]所需的最少操作次数。 - 递推关系式:
- 如果
word1[i-1] == word2[j-1],则不需要任何操作:dp[i][j] = dp[i-1][j-1] - 否则,需要进行插入、删除或替换操作之一,取最小值并加1:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
- 如果
Python实现
def edit_distance(word1, word2):
m = len(word1)
n = len(word2)
# 创建一个 (m+1) x (n+1) 的二维数组来存储子问题的解
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
# 填充dp数组
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]
以上就是动态规划的经典问题以及它们的解决思路和实现方法。这些问题是学习和理解动态规划的关键步骤,也是算法面试中常见的题目类型。