思想 & 基本要素
先讲一个问题来了解动态规划算法的思想
矩阵连乘问题
问题描述:给定 n 个矩阵 { A1, A2, … , An } 其中相邻的矩阵是可乘的,求它们的连乘积 A1, A2, … , An
完全加括号:以加括号的形式,明确指明矩阵连乘的计算顺序,记为 ( A1, A2, … , An )
不同的完全加括号式对应不同的运算次数
矩阵连乘问题即为寻找运算次数最小的完全加括号式
穷举搜索法
再思考下列问题:
分治算法的三个要点:子问题与原问题的性质相同,子问题的求解彼此独立,划分子问题的规模尽可能均衡
若分解的子问题不是互相独立的?
分治法的子问题如下图:
动态规划的子问题如下图:
重复计算导致复杂性急剧提高,甚至达到指数阶
Catalan数列
指数阶
求解中子问题存在大量的重复
实际上所有的子问题只有10个
A1, A2, A3, A4, A1A2, A2A3, A3A4, A1A2A3, A2A3A4, A1A2A3A4
动态规划的两个基本要素
第一个要素:最优子结构性质
- 问题的最优解包含了子问题的最优解
- 原问题可以通过分解子问题来解决
第二个要素:重叠子问题性质(如:爬楼梯问题、斐波那契数列)
与分治法的主要区别: - 直接递归求解子问题的复杂性往往是指数阶
- 子问题空间的规模通常是多项式阶
- 同一个子问题可能出现在多次问题分解中
动态规划的求解过程
递归 --> 保存 --> 自底向上 / 自顶向下
矩阵连乘问题的求解
动态规划的复杂性为 O(n^3)
备忘录法:自顶向下
动态规划与备忘录
- 动态规划相当于迭代,需要分析子问题的结构,从最小的问题解起
- 备忘录相当于递归,直接从原问题出发,若未解,则向下递归调用
- 当所有子问题都至少需要求解一次时,动态规划较好
- 当子问题空间中的部分子问题不必求解时,备忘录较好
最长公共子序列
问题描述:求序列 X = { x1, x2, … , xm} 和序列 Y = {y1, y2, … , yn} 最长的公共子序列
穷举法
找出 X = { x1, x2, … , xm} 的所有子序列,逐个检查 X子序列是否也是 Y子序列,输出最长的一个公共子序列,子序列的数量为 O(2^m)
最优子结构性质
设 X = { x1, x2, … , xm} 和序列 Y = {y1, y2, … , yn} 的最长公共子序列为 Z = {z1, z2, … , zk} ,则
- Zk-1 是 Xm-1 和 Yn-1 的最长公共子序列
- Z 是 Xm-1 和 Y 的最长公共子序列
- Z 是 X 和 Yn-1 的最长公共子序列
重叠子问题性质
递归关系式
动态规划求解
算法的复杂性为 O(mn)
0-1背包问题
问题描述:给定 n 个物品和一个背包。物品 i 的重量为 Wi ,价值为 Vi ,背包的容量为 c ,问如何选择物品,使得背包中物品的价值最大
动态规划法
1、最优子结构
设 { x1, x2, … , xn} 是背包的一种最优装法,考虑如下子问题:
待装入物品 { 2, … , n} ,且背包容量为 c-W1X1
假设这个子问题的最优解不是 { x2, … , xn},而是 { x’2, … , x’n},则原问题按 { x1, x‘2, … , x’n} 的装法,得到的总价值最高,与 { x1, x2, … , xn} 是最优装法的假设矛盾
2、子问题构造
已考察完后 n - i 个物品,剩余物品为 1, … , i ,剩余容量为 j
设该子问题的最优解为 m[ i ][ j ]
,原问题为 m[ 0 ][ c ]
3、递归关系式
考察下一个物品 i是否装入,若 Wi > j,无法装入,否则,取装入时和不装入时价值的较大者
算法的复杂性为 O(nc)
小结
- 使用动态规划求解时,应先证明问题的最优子结构性质
- 构造子问题最优解之间的递归关系式仍是求解的重点和难点
- 子问题的数量通常是规模的多项式阶
- 绘制一维或二维 DP 表是动态规划的常用手段