【算法】浅析动态规划

动态规划介绍与使用

1. 算法定义

动态规划(Dynamic Programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

2. 诞生背景

动态规划诞生于20世纪50年代,由美国数学家理查德·贝尔曼提出。当时,为了解决多阶段决策问题,贝尔曼提出了动态规划这一概念。

3. 发展历程

自贝尔曼提出动态规划以来,这一方法在多个领域得到了广泛的应用和发展。以下是动态规划的主要发展历程:

  1. 1950年代:贝尔曼提出动态规划基本理论。
  2. 1960年代:动态规划在运筹学、控制理论等领域得到应用。
  3. 1970年代:动态规划开始应用于计算机科学领域,如算法设计。
  4. 1980年代至今:动态规划在人工智能、生物信息学等领域取得显著成果。

4. 特点

动态规划具有以下特点:

  1. 最优子结构:问题的最优解包含其子问题的最优解。
  2. 子问题重叠:动态规划将原问题分解为多个子问题,这些子问题不是独立的,而是重叠的。
  3. 无后效性:某阶段的状态一旦确定,就不受之后阶段的影响。

5. 基本原理

动态规划的基本原理是将复杂问题分解为若干个相互重叠的子问题,从最简单的子问题开始求解,并将子问题的解存储起来,避免重复计算。最后,通过子问题的解得到原问题的解。

6. 应用领域

动态规划在以下领域有广泛应用:

  1. 计算机科学:算法设计、字符串匹配、图像处理等。
  2. 经济学:资源分配、投资策略、价格决策等。
  3. 生物信息学:基因序列比对、蛋白质结构预测等。

7. 实例理解

以斐波那契数列为例,其递推公式为:F(n) = F(n-1) + F(n-2),其中F(0) = 0,F(1) = 1。

F(0) = 0
F(1) = 1
F(2) = F(1) + F(0) = 1
F(3) = F(2) + F(1) = 2
F(4) = F(3) + F(2) = 3
...

通过动态规划,我们可以将F(n)分解为F(n-1)和F(n-2)两个子问题,从而避免重复计算。

8. 代码示例

以下是一个使用Python实现的斐波那契数列的动态规划代码示例:

def fibonacci(n):
    if n <= 1:
        return n
    dp = [0] * (n+1)
    dp[1] = 1
    for i in range(2, n+1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[n]
print(fibonacci(10))

9. 代码分步讲解

  1. 定义一个名为fibonacci的函数,参数为n,表示求斐波那契数列的第n项。
  2. 判断n的值,如果小于等于1,直接返回n
  3. 创建一个长度为n+1的列表dp,用于存储子问题的解。
  4. 初始化dp[1] = 1,因为斐波那契数列的第二项为1。
  5. 使用一个for循环,从2遍历到n,计算每个子问题的解,并存储在dp列表中。
  6. 返回dp[n],即斐波那契数列的第n项。

通过以上步骤,我们成功使用动态规划求解了斐波那契数列问题。

10. 实例理解(进阶)

下面我们将使用动态规划来解决一个相对复杂的问题:最长公共子序列(Longest Common Subsequence, LCS)问题。这个问题是寻找两个或多个序列中最长的子序列,该子序列在原序列中是按相同顺序出现的,但不要求连续。

示例:

input:str1 = 123456 
       str2 = 234567

output:LCS = 23456

1. 问题定义

给定两个字符串 str1str2,找出它们的最长公共子序列的长度。

2. 动态规划思路

  1. 创建一个二维数组 dp,其中 dp[i][j] 表示 str1 的前 i 个字符和 str2 的前 j 个字符的最长公共子序列的长度。
  2. 初始化 dp 的第一行和第一列为0,因为任何一个字符串与空字符串的最长公共子序列长度都是0。
  3. 遍历 str1str2 的字符,如果当前字符相等,则 dp[i][j] = dp[i-1][j-1] + 1;否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  4. 最终 dp[str1.length()][str2.length()] 就是两个字符串的最长公共子序列的长度。

3. 代码分段

初始化部分
def longest_common_subsequence(str1, str2):
    m, n = len(str1), len(str2)
    # 创建一个 (m+1)x(n+1) 的二维数组,初始化为0
    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 str1[i - 1] == str2[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]

4. 整体代码

def longest_common_subsequence(str1, str2):
    # 获取两个字符串的长度
    m, n = len(str1), len(str2)
    
    # 初始化动态规划表,大小为 (m+1)x(n+1),初始值为0
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    # 填充动态规划表
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            # 如果当前字符相等,更新 dp 表的值
            if str1[i - 1] == str2[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]
# 测试代码
str1 = "ABCBDAB"
str2 = "BDCAB"
print(longest_common_subsequence(str1, str2))  # 输出应为4,最长公共子序列是 "BCAB"

以上代码展示了如何使用动态规划来解决最长公共子序列问题。代码中的注释说明了每一部分的作用和解决问题的思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值