通常用来解决最优化问题。在做出每个选择的同时,通常会生成与原问题形式相同的子问题。当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会非常有效。其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解。
分治:划分为互不相交的子问题,递归求解子问题,再将他们的解组合起来。
动态规划(dynamic programming,表格法而非编程)用于子问题重叠的情况。
四个步骤来设计一个动态规划算法:
1 刻画一个最优解的结构特征
2 递归地定义最优解的值
3 计算最优解的值,通常采用自底向上的方法
4 利用计算出来的信息构造一个最优解
最优子结构:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
实现方法:
带备忘的自顶向下法(top-down with memoization)
自底向上法(bottom-up method), 复杂度系数更小。
15.3 动态规划原理
适合应用动态规划方法求解的最优化问题应具备的两个要素:最优子结构和子问题重叠。
发掘最优子结构的通用模式:
做出一个选择。它会产生一个或多个待解的子问题。
在可能的第一步选择中,假定已经知道哪种选择会得到最优解
确定这次选择会产生哪些子问题,以及如何最好的刻画子问题空间
每个子问题的解就是它本身的最优解。
一个刻画子问题空间的好经验:保持子问题空间尽可能简单,只有必要时才扩展它。
对于不同问题领域,最优子结构的不同体现在两个方面:最优解用到的子问题总数和确定最优选择时需要考察多少种选择。
可用其乘积来粗略分析运行时间。
子问题必须无关(最长简单路径问题就不能使用动态规划)。如果求解一个子问题用到了某些资源,导致这些资源在求解其他子问题时不可用,则不能动态规划。
如果每个子问题都必须至少求解一次:自底向上
如果子问题空间的某些子问题完全不必求解:自顶向下(递归方式+备忘录)
15.4 最长公共子序列(LCS)
一个给定序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。即不要求连续,故不同于子串,但类似于模糊匹配中的*。
定理15.1(LCS的最优子结构) 令X=<x1,x2,...,xm>,Y=<y1,y2,...,yn>为两个序列,Z=<z1,z2,...,zk>为X和Y的任意LCS。
1. 如果xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的一个LCS。
2. 如果xm!=yn,则zk!=xm意味着Zk-1是Xm-1和Y的一个LCS。
3. 如果xm!=yn,则zk!=yn意味着Zk-1是X和Yn-1的一个LCS。
可以用m*n的数组或者约max(m,n)+O(1)的空间来查找到LCS的长度。如需重构,为了保证O(m+n)的重构时间,则大约需要m*n空间。
15.5 最优二叉搜索树
形式化定义:给定一个n个不同关键字的已排序的序列K=<k1,k2,...,kn>(因此k1<k2<...<kn),我们希望用这些关键字构造一棵二叉树。对每个关键字ki,都有一个概率pi表示其搜索概率。n个关键字及n+1个不存在的伪关键字都带搜索概率。搜索代价最小的二叉搜索树,叫做最优二叉搜索树。
不一定是高度最矮的,概率最高的关键字也不一定出现在二叉搜索树的根结点。
步骤1:最优二叉搜索树的结构
任意子树包含连续关键字ki,...,kj,叶节点di-1,...,dj(失败搜索)。
最优子结构:如果一个最优二叉树T有一棵包含关键字ki,...,kj的子树T’,那么T’必然是包含关键字ki,...,kj和伪关键字di-1,...,dj的子问题的最优解。
步骤2:一个递归算法
注意成为子树后高度增加1
步骤3:计算最优二叉搜索树的期望搜索代价