动态规划方法分析 | 豆包MarsCode AI刷题

113 阅读3分钟

动态规划是一种用于解决优化问题的算法策略,主要思想是将一个复杂的问题分解为一系列相互关联的子问题,并通过求解子问题的最优解来构建原问题的最优解。

  1. 分析问题结构

    • 首先要确定问题是否具有最优子结构性质。这意味着问题的最优解可以由子问题的最优解组合而成。例如,在计算斐波那契数列时,第 n 个斐波那契数可以通过第 n - 1 和 n - 2 个斐波那契数相加得到,这就体现了最优子结构。
    • 还要考虑子问题是否重叠。很多动态规划问题中,子问题会被多次计算。比如在计算斐波那契数列的递归实现中,会有大量重复计算相同的子项。
  2. 定义状态

    • 状态是对问题在某个阶段的描述。以背包问题为例,状态可以是考虑前 i 个物品,背包容量为 j 时的最大价值。状态的定义要能够完整地描述子问题,并且方便状态转移方程的推导。
    • 状态通常用一个或多个变量来表示,这些变量应该能够反映出问题的关键因素。
  3. 建立状态转移方程

    • 状态转移方程描述了如何从一个或多个子状态得到当前状态的最优解。在最长公共子序列问题中,设两个序列为 A 和 B,状态转移方程为:如果 A [i]==B [j],那么 dp [i][j]=dp [i - 1][j - 1]+1;否则 dp [i][j]=max (dp [i - 1][j], dp [i][j - 1])。
    • 它是动态规划的核心,通过这个方程可以递归或迭代地求解问题。
  4. 确定边界条件

    • 边界条件是问题的最基础情况。在斐波那契数列中,边界条件是 dp [0]=0 和 dp [1]=1。
    • 这些条件是递归或迭代的起点,确保状态转移方程在起始阶段有明确的定义。
  5. 选择求解方式(自顶向下或自底向上)

    • 自顶向下(记忆化搜索) :这种方法从原始问题开始,通过递归将问题分解为子问题。使用一个记忆化数组来存储已经计算过的子问题的解,避免重复计算。例如,在计算斐波那契数列时,可以创建一个数组来存储已经计算过的斐波那契数。
    • 自底向上(迭代) :从最基础的子问题开始,逐步向上求解,直到得到原始问题的解。还是以斐波那契数列为例,通过一个循环从最开始的两个数逐步计算出后续的数。这种方式通常更高效,因为它避免了递归调用的开销。
  6. 时间和空间复杂度分析

    • 时间复杂度主要取决于状态的数量和状态转移方程的计算时间。在很多情况下,动态规划的时间复杂度是多项式级别的,例如背包问题的时间复杂度通常是 O (nW),其中 n 是物品数量,W 是背包容量。
    • 空间复杂度取决于存储状态所需要的空间。有时候可以通过优化状态存储方式来降低空间复杂度,例如使用滚动数组,在不影响计算结果的情况下减少存储量。