使用dp数组解决简单的动态规划问题 | 豆包MarsCode AI刷题
动态规划(Dynamic Programming, DP)是一种通过将复杂问题分解为更小的子问题来解决问题的方法。使用动态规划的关键在于定义状态和状态转移方程。下面通过几个经典问题来说明如何使用 dp 数组解决简单的动态规划问题。从最基础的爬楼梯问题开始对dp数组进行简单介绍,如何结合青训营AI刷题中的部分题目对dp数组用于处理动态规划问题进行更深刻的理解。
爬楼梯问题
问题描述:
假设你正在爬楼梯。需要 n 步才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶?
状态定义:
dp[i] 表示到达第 i 个台阶的方法数。 状态转移方程:
dp[i] = dp[i-1] + dp[i-2],因为到达第 i 个台阶可以从第 i-1 个台阶爬一步,或者从第 i-2 个台阶爬两步。 初始条件:
dp[0] = 1(到达第 0 个台阶有 1 种方法,即不动)。 dp[1] = 1(到达第 1 个台阶有 1 种方法,即爬 1 个台阶)。
代码实现
def climbStairs(n: int) -> int:
if n == 0: return 1
if n == 1: return 1
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
DNA序列编辑距离问题
问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
测试样例
样例1:
输入:
dna1 = "AGT",dna2 = "AGCT"
输出:1
样例2:
输入:
dna1 = "AACCGGTT",dna2 = "AACCTTGG"
输出:4
样例3:
输入:
dna1 = "ACGT",dna2 = "TGC"
输出:3
样例4:
输入:
dna1 = "A",dna2 = "T"
输出:1
样例5:
输入:
dna1 = "GGGG",dna2 = "TTTT"
输出:4
对于上述问题,我们可以结合在爬楼梯问题中对于dp数组的了解,给出以下解决思路
状态定义
dp[i][j] 表示将 dna1 的前 i 个字符转换成 dna2 的前 j 个字符所需的最少编辑步骤。
状态转移方程
如果 dna1[i-1] == dna2[j-1],则 dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。 否则,dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1。
其中:
dp[i-1][j] + 1 表示删除 dna1 的第 i 个字符。
dp[i][j-1] + 1 表示在 dna1 的第 i 个字符后面插入一个字符。
dp[i-1][j-1] + 1 表示替换 dna1 的第 i 个字符。
初始条件
dp[i][0] = i,表示将 dna1 的前 i 个字符转换成空字符串,需要删除 i 个字符。
dp[0][j] = j,表示将空字符串转换成 dna2 的前 j 个字符,需要插入 j 个字符。
代码实现
def solution(dna1, dna2):
# Please write your code here
m, n =len(dna1), len(dna2)
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 dna1[i -1] == dna2[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]
if __name__ == "__main__":
# You can add more test cases here
print(solution("AGCTTAGC", "AGCTAGCT") == 2 )
print(solution("AGCCGAGC", "GCTAGCT") == 4)