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\) 的多米诺骨牌)完全覆盖网格,求方案数。
- 这类问题的状态设计往往需要利用状态压缩,用整数的二进制位来表示某一列(或某一行)的占用情况,从而在逐格或逐列推进时进行转移。
浙公网安备 33010602011771号