CPH-第七章 动态规划

动态规划是一种结合了穷举搜索的正确性与贪心算法的效率的技术。

  • 动态规划适用的前提:问题可以分解为若干个相互重叠的子问题,且这些子问题能够独立求解。
  • 动态规划通常用于:
    • 寻找最优解
    • 计算方案数

7.1 硬币问题(Coin Problem)

最少硬币数

  • 设 \(solve(x)\) 为凑出金额 \(x\) 所需的最少硬币数。状态转移方程为:

\[solve(x) = \begin{cases} \infty & x < 0 \\ 0 & x = 0 \\ \min_{c \in \text{coins}} solve(x-c) + 1 & x > 0 \end{cases}\]

  • 朴素的递归会反复计算相同的 \(solve(x)\)。

  • 动态规划通过一个数组 value[x] 自底向上计算,避免了这种冗余。

  • 其中 value[0] = 0,对于无法凑出的金额可设为无穷大。

方案计数

  • 同一框架也可用于计数。如果 \(solve(x)\) 表示凑出金额 \(x\) 的方案总数,状态转移方程为:

\[solve(x) = \begin{cases} 0 & x < 0 \\ 1 & x = 0 \\ \sum_{c \in \text{coins}} solve(x-c) & x > 0 \end{cases}\]

  • 若硬币顺序不同视为不同方案,先遍历金额,再遍历硬币。

  • 若硬币顺序不同不视为不同方案,则需调整遍历顺序(先遍历硬币,再遍历金额),以避免重复计数。

图论视角

  • 硬币问题可以自然地表示为一个有向无环图(DAG):
    • 每个节点代表一个金额,每条边代表使用一枚硬币。
    • 凑出金额 \(n\) 就对应于从节点 0 到节点 \(n\) 的一条路径。
    • 最少硬币数对应最短路径,
    • 方案总数对应路径计数。

7.2 最长递增子序列(Longest Increasing Subsequence)

给定一个数组,要求找出最长的递增子序列的长度。

  • 设 \(length(i)\) 为以第 \(i\) 个元素结尾的最长递增子序列长度,状态转移为:

\[length(i) = \max_{j < i,\ a_j < a_i} length(j) + 1 \]

  • 基础情况为 \(length(i) = 1\)(每个元素自身构成一个长度为 1 的子序列)。
  • 其他情况为 a[i] 接在 a[j] 后面
  • 最终答案是所有 \(length(i)\) 中的最大值。
  • 朴素实现为 \(O(n^2)\);结合二分查找可优化至 \(O(n \log n)\)。

7.3 网格中的路径(Paths in a Grid)

在一个 \(n \times m\) 的网格中,从左上角移动到右下角,每次只能向右或向下移动一步。

  • 设 paths[i][j] 为到达格子 \((i, j)\) 的路径数。

  • 由于到达 \((i, j)\) 只能来自上方或左方,因此状态转移方程为:

\[paths[i][j] = paths[i-1][j] + paths[i][j-1] \]

  • 边界条件:第一行和第一列的所有格子都只有 1 条路径。

7.4 背包问题(Knapsack Problems)

背包问题是指给定一组物品(每个物品有重量和价值),要求选取一个子集,在满足某些约束(如总重量不超过容量)的前提下,最优化某个目标(如总价值最大)。

0-1 背包

  • 每个物品只能选取一次。设 dp[i][j] 为在前 \(i\) 个物品中选择,背包容量为 \(j\) 时能获得的最大价值。

  • 转移方程式:

\[dp[i][j] = \max(dp[i - 1][j],\ dp[i - 1][j - w_i] + v_i) \]

  • 空间降维

\[dp[j] = \max(dp[j], dp[j - w_i]) \]

  • 逆序遍历保证了在更新 dp[j] 时,所依赖的 dp[j - w_i] 尚未被当前物品更新过。

完全背包

  • 每个物品可以选取无限次。此时只需将容量 \(j\) 的遍历改为正序,从而允许同一物品被多次加入。

7.5 编辑距离(Edit Distance)

给定两个字符串,要求用最少的操作(插入、删除、替换一个字符)将一个字符串变为另一个。

  • 这是一个在两个序列上定义状态的经典 DP 问题。

  • 设 dist[i][j] 为将第一个字符串的前 \(i\) 个字符转换为第二个字符串的前 \(j\) 个字符所需的最少操作数。

  • 状态转移考虑末尾字符是否匹配:

    • 若匹配:dist[i][j] = dist[i-1][j-1]
    • 若不匹配:dist[i][j] = 1 + min(dist[i-1][j], dist[i][j-1], dist[i-1][j-1])(分别对应删除、插入、替换)

7.6 计数平铺(Counting Tilings)

给定一个 \(n \times m\) 的网格,要求用若干种规格的骨牌(如 \(1 \times 2\) 的多米诺骨牌)完全覆盖网格,求方案数。

  • 这类问题的状态设计往往需要利用状态压缩,用整数的二进制位来表示某一列(或某一行)的占用情况,从而在逐格或逐列推进时进行转移。
posted @ 2026-10-30 13:17  LifeLotus  阅读(1)  评论(0)    收藏  举报