深度学习速通系列:动态规划

发布于:2024-12-20 ⋅ 阅读:(179) ⋅ 点赞:(0)

动态规划(Dynamic Programming,简称DP)是一种用于解决最优化问题的算法设计方法。它通过将复杂问题分解成更小的子问题,并存储已解决的子问题结果来避免重复计算,从而有效地提高计算效率。

动态规划的基本思想:

  1. 问题的最优子结构:原问题的最优解可以通过子问题的最优解来推导出来。
  2. 子问题的重叠性:原问题的解可以由子问题的解组合而成,这些子问题会多次计算,因此需要保存子问题的解来避免重复计算。

动态规划的步骤:

  1. 定义状态:确定问题中的子问题状态,通常通过一个或多个变量来表示子问题的解。
  2. 状态转移方程:通过已知的子问题的解来计算出当前问题的解。
  3. 边界条件:确定最小子问题的解,这通常是已知的初始值。
  4. 计算顺序:根据状态转移方程计算问题的解,通常是通过自底向上的方式,先解决最小子问题,再逐步解决较大的子问题,直到解决原问题。

动态规划的类型:

  1. 自顶向下(递归 + 记忆化):通过递归求解每个子问题,并利用备忘录(数组或字典)来存储子问题的结果,避免重复计算。
  2. 自底向上(迭代):从最简单的子问题开始,通过迭代逐步求解更大的子问题,直到得到原问题的解。

经典的动态规划问题:

  1. 背包问题:给定一组物品,每个物品有一个重量和价值,背包有一个最大承重,求能够装入背包的最大价值。
  2. 最长公共子序列(LCS):给定两个字符串,找出它们的最长公共子序列。
  3. 最短路径问题:如Dijkstra算法或Floyd-Warshall算法,用来解决图中最短路径的计算。
  4. 矩阵链乘法:给定一系列矩阵,求出最小的计算代价(最少的乘法次数)来计算这些矩阵的乘积。
  5. 爬楼梯问题:给定n阶楼梯,每次可以走1阶或2阶,问走到第n阶有多少种方法。

动态规划的实现示例:

0-1背包问题为例,给出动态规划的解法。

问题描述:给定n个物品,每个物品有一个重量和价值,背包的容量为C,求最大价值。

def knapsack(weights, values, capacity):
    n = len(weights)
    # 创建一个二维dp数组,dp[i][j]表示前i个物品,背包容量为j时的最大价值
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]

    # 填充dp表
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            # 不放第i个物品
            dp[i][w] = dp[i - 1][w]
            # 放第i个物品,且背包容量大于物品重量
            if w >= weights[i - 1]:
                dp[i][w] = max(dp[i][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])

    return dp[n][capacity]

# 示例
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 5
print(knapsack(weights, values, capacity))  # 输出 7

解释

  • dp[i][w]表示前i个物品、背包容量为w时的最大价值。
  • 对于每个物品,可以选择放入背包或不放入背包,转移方程为:
    [
    dp[i][w] = \max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])
    ]
  • 最终的解即为dp[n][capacity]

动态规划的时间与空间复杂度:

  • 时间复杂度:通常是O(n * W),其中n是物品的数量,W是背包的容量。对于大多数动态规划问题,复杂度取决于子问题的数量。
  • 空间复杂度:O(n * W),通常需要存储每个子问题的解。如果空间有限,可以通过优化空间复杂度来减少内存使用,例如通过滚动数组将空间从O(n * W)降到O(W)。

动态规划的核心是“记住之前的计算结果”,它避免了重复计算,是解决很多最优化问题的有效工具。


网站公告

今日签到

点亮在社区的每一天
去签到