动态规划(Dynamic Programming, DP)

简介: 动态规划(Dynamic Programming, DP)

动态规划(Dynamic Programming, DP)是一种通过将问题分解为更小的子问题,并存储这些子问题的解来避免重复计算的方法。它通常用于解决具有重叠子问题和最优子结构性质的问题。

1. 基本概念

动态规划的核心思想是将复杂问题分解为简单的子问题,并通过存储这些子问题的解来提高效率。动态规划通常使用一个表格(数组或字典)来存储子问题的解,以避免重复计算。

2. 示例:斐波那契数列

斐波那契数列是一个经典的动态规划问题。我们可以通过自底向上的方式实现它,从而避免递归中的大量重复计算。

实现代码

def fibonacci(n):
    if n <= 0:
        return 0
    elif n == 1:
        return 1

    # 创建一个数组来存储子问题的解
    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(6))  # 输出 8

在这个例子中,我们使用一个数组 dp 来存储从 0n 的斐波那契数。通过迭代填充这个数组,我们避免了递归中的重复计算。

3. 示例:背包问题

背包问题是另一个经典的动态规划问题。假设你有一个容量为 W 的背包和 n 个物品,每个物品有一个重量和一个价值。目标是选择一些物品放入背包,使得总价值最大且总重量不超过 W

实现代码

def knapsack(weights, values, W):
    n = len(weights)
    # 创建一个二维数组来存储子问题的解
    dp = [[0 for _ in range(W + 1)] for _ in range(n + 1)]

    # 填充数组
    for i in range(1, n + 1):
        for w in range(W + 1):
            if weights[i - 1] <= w:
                dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])
            else:
                dp[i][w] = dp[i - 1][w]

    return dp[n][W]

# 测试
weights = [1, 2, 3]
values = [6, 10, 12]
W = 5
print(knapsack(weights, values, W))  # 输出 22

在这个例子中,我们使用一个二维数组 dp 来存储子问题的解。dp[i][w] 表示前 i 个物品在容量为 w 的背包中的最大价值。通过迭代填充这个数组,我们可以找到最优解。

4. 示例:最长公共子序列(LCS)

最长公共子序列是另一个常见的动态规划问题。给定两个字符串,找到它们的最长公共子序列的长度。

实现代码

def longest_common_subsequence(X, Y):
    m = len(X)
    n = len(Y)
    # 创建一个二维数组来存储子问题的解
    dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]

    # 填充数组
    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]

# 测试
X = "AGGTAB"
Y = "GXTXAYB"
print(longest_common_subsequence(X, Y))  # 输出 4

在这个例子中,我们使用一个二维数组 dp 来存储子问题的解。dp[i][j] 表示字符串 X 的前 i 个字符和字符串 Y 的前 j 个字符的最长公共子序列的长度。通过迭代填充这个数组,我们可以找到最长公共子序列的长度。

5. 注意事项

  • 状态定义:明确定义子问题的状态,这是动态规划的关键。
  • 状态转移方程:找出子问题之间的关系,即状态转移方程。
  • 初始条件:确定边界条件,通常是最小的子问题。
  • 空间优化:有时可以通过滚动数组等技术来优化空间复杂度。

通过这些示例和注意事项,你可以在 Python 中有效地实现和使用动态规划来解决各种复杂的问题。

目录
相关文章
|
人工智能 算法 安全
详解贪心算法
详解贪心算法
|
10月前
|
机器学习/深度学习 数据采集 搜索推荐
使用Python实现智能食品消费偏好预测的深度学习模型
使用Python实现智能食品消费偏好预测的深度学习模型
322 23
|
机器学习/深度学习 运维 监控
函数计算产品使用问题之如何解决SD插件安装后不显示的问题
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
236 0
|
存储 SQL 数据库连接
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
390 13
|
存储 SQL 数据库连接
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
231 2
|
编译器 API C语言
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
892 0
|
开发框架 前端开发 JavaScript
基于SSM框架实现宠物领养系统的设计与实现
基于SSM框架实现宠物领养系统的设计与实现
631 3
基于SSM框架实现宠物领养系统的设计与实现
|
JavaScript BI
技术笔记:JS获取子节点、父节点和兄弟节点的方法实例总结
技术笔记:JS获取子节点、父节点和兄弟节点的方法实例总结
421 0
|
编解码 算法 程序员
老程序员分享:OpenGL学习进程(10)第七课:四边形绘制与动画基础
老程序员分享:OpenGL学习进程(10)第七课:四边形绘制与动画基础