目录
案例一:斐波那契数列
案例二:最大子数组和
案例三:莱文斯坦距离
动态规划(Dynamic Programming)是一种解决复杂问题的算法设计思想,通常用于解决具有重叠子问题和最优子结构性质的问题。它将问题分解成多个小问题,通过解决小问题来解决原始问题。
动态规划的基本思想可以总结为以下几步:
-
确定状态:首先要确定问题的状态,也就是问题的规模。状态的定义往往和问题的具体描述相关,它可以是一个整数、一个数组、一个矩阵等。
-
定义状态转移方程:接下来要确定状态之间的转移关系,也就是如何从一个状态转移到另一个状态。这是动态规划问题的核心。状态转移方程描述了问题的子问题之间的关系,通过它可以推导出问题的解。
-
初始化:通常需要初始化一些状态,使得问题能够顺利进行。这是动态规划问题的初始条件。
-
递推计算:根据状态转移方程,通过递推的方式计算出问题的最终解。
-
解的表示:根据具体的问题,确定最终解的表示方式。可能是某一个状态的值,也可能是一些状态的组合。
-
时间复杂度分析:分析动态规划算法的时间复杂度,通常动态规划算法的时间复杂度与状态数和每个状态的转移时间复杂度有关。
动态规划的主要优点是能够在解决问题时避免重复计算,通过利用已经计算过的结果来加速求解过程,因此它往往能够高效地解决很多复杂的问题。
案例一:斐波那契数列
斐波那契数列是一个经典的数学问题,定义如下:
- F(0) = 0, F(1) = 1
- F(n) = F(n-1) + F(n-2),其中 n > 1
方法:我们可以使用一个数组来存储已经计算过的斐波那契数值,避免重复计算。
def fibonacci(n):
if n <= 1:
return n
# 创建一个数组来存储已经计算过的斐波那契数值
fib = [0] * (n + 1)
fib[1] = 1
for i in range(2, n + 1):
fib[i] = fib[i - 1] + fib[i - 2]
return fib[n]
# 测试
result = fibonacci(10)
print(result) # 输出 55
案例二:最大子数组和
给定一个整数数组,我们需要找到一个连续子数组,使得子数组的和最大。
方法:使用了动态规划思想,设置两个变量,current_max
和 global_max
,分别代表当前的最大和全局的最大。
具体的算法如下:
- 初始化
current_max
和global_max
为数组第一个元素的值。 - 从数组的第二个元素开始遍历:
-
current_max
更新为当前元素值与当前元素值加上current_max
的较大值。 - 如果
current_max
大于global_max
,则更新global_max
。
-
- 遍历完成后,
global_max
就是最大子数组的和。
def max_subarray_sum(nums):
current_max = global_max = nums[0]
for i in range(1, len(nums)):
current_max = max(nums[i], current_max + nums[i])
if current_max > global_max:
global_max = current_max
return global_max
# 测试
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
result = max_subarray_sum(nums)
print(result) # 输出 6
案例三:莱文斯坦距离
莱文斯坦距离是衡量两个字符串之间相似程度的一种度量方法。给定两个字符串 word1 和 word2,我们可以进行三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
我们需要找到将 word1 转换为 word2 所需的最少操作次数。
方法:使用动态规划来解决。我们可以使用一个二维数组 dp
,其中 dp[i][j]
表示将 word1
的前 i
个字符转换为 word2
的前 j
个字符所需的最少操作次数。
具体算法如下:文章来源:https://uudwc.com/A/rZA5J
- 初始化一个
(len(word1) + 1) x (len(word2) + 1)
的二维数组dp
。 - 初始化边界条件,即当一个字符串为空时,将另一个字符串全部插入即可。
- 使用动态规划递推公式填充
dp
数组:- 如果
word1[i-1] == word2[j-1]
,则dp[i][j] = dp[i-1][j-1]
,表示不需要进行任何操作。 - 否则,
dp[i][j]
可以通过插入、删除、替换三种操作中的一种得到:- 插入操作:
dp[i][j] = dp[i][j-1] + 1
- 删除操作:
dp[i][j] = dp[i-1][j] + 1
- 替换操作:
dp[i][j] = dp[i-1][j-1] + 1
- 插入操作:
- 取三种操作中的最小值作为最终结果。
- 如果
-
dp[len(word1)][len(word2)]
就是将word1
转换为word2
所需的最少操作次数。
def edit_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 - 1], dp[i][j - 1], dp[i - 1][j]) + 1
return dp[m][n]
# 测试
word1 = "intention"
word2 = "execution"
result = edit_distance(word1, word2)
print(result) # 输出 5
案例分析2:动态规划——经典案例分析2文章来源地址https://uudwc.com/A/rZA5J