动态规划——经典案例分析

目录

案例一:斐波那契数列

案例二:最大子数组和

案例三:莱文斯坦距离


动态规划(Dynamic Programming)是一种解决复杂问题的算法设计思想,通常用于解决具有重叠子问题和最优子结构性质的问题。它将问题分解成多个小问题,通过解决小问题来解决原始问题。

动态规划的基本思想可以总结为以下几步:

  1. 确定状态:首先要确定问题的状态,也就是问题的规模。状态的定义往往和问题的具体描述相关,它可以是一个整数、一个数组、一个矩阵等。

  2. 定义状态转移方程:接下来要确定状态之间的转移关系,也就是如何从一个状态转移到另一个状态。这是动态规划问题的核心。状态转移方程描述了问题的子问题之间的关系,通过它可以推导出问题的解。

  3. 初始化:通常需要初始化一些状态,使得问题能够顺利进行。这是动态规划问题的初始条件。

  4. 递推计算:根据状态转移方程,通过递推的方式计算出问题的最终解。

  5. 解的表示:根据具体的问题,确定最终解的表示方式。可能是某一个状态的值,也可能是一些状态的组合。

  6. 时间复杂度分析:分析动态规划算法的时间复杂度,通常动态规划算法的时间复杂度与状态数和每个状态的转移时间复杂度有关。

动态规划的主要优点是能够在解决问题时避免重复计算,通过利用已经计算过的结果来加速求解过程,因此它往往能够高效地解决很多复杂的问题。 


案例一:斐波那契数列

斐波那契数列是一个经典的数学问题,定义如下:

  • 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_maxglobal_max,分别代表当前的最大全局的最大。 

具体的算法如下:

  1. 初始化 current_maxglobal_max 为数组第一个元素的值。
  2. 从数组的第二个元素开始遍历:
    • current_max 更新为当前元素值与当前元素值加上 current_max 的较大值。
    • 如果 current_max 大于 global_max,则更新 global_max
  3. 遍历完成后,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,我们可以进行三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

我们需要找到将 word1 转换为 word2 所需的最少操作次数。

方法:使用动态规划来解决。我们可以使用一个二维数组 dp,其中 dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最少操作次数。

具体算法如下:

  1. 初始化一个 (len(word1) + 1) x (len(word2) + 1) 的二维数组 dp
  2. 初始化边界条件,即当一个字符串为空时,将另一个字符串全部插入即可。
  3. 使用动态规划递推公式填充 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
    • 取三种操作中的最小值作为最终结果。
  4. 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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恭仔さん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值