动态规划
动态规划(DP)算法通常用来求解最优化问题
DP与分治相似,都是通过组合子问题的解来求解原问题。但DP应用于子问题重叠的情况,即不同的子问题拥有共同的子子问题。在这种情况下,分治算法会做很多不必要的工作,它会反复求解那些公共子子问题。而DP对每个子子问题之求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题是都重新计算,避免了这种不必要的计算。
动态规划算法的步骤:
- 刻画一个最优解的结构特征
- 递归地定义最优解的值(求解状态转移方程)
- 计算最优解的值,通常采用自底向上的方法
- 利用计算出的信息构造一个最优解
最后一步过程中维护一些额外的信息,以便于构造一个最优解
动态规划方法对每个子问题之求解一次,并将结果保存下来,如果随后再次需要求解此子问题的姐,则只需要查找保存的结果,而不必重新计算。用空间换时间,但在时间上的节省确是非常巨大的:有事可以将一个指数时间的姐转化成一个多项式时间的解
动态规划有两种等价的实现方法:
- 带备忘录的自顶向下法(top-down with memoization)
- 自顶向上法(bottom-up method)
带备忘录的自顶向下法::
仍然按照自然的递归形式编写过程,但过程会保存每个子问题的解(通常是保存在一个数组或者散列表中)。当需要一个子问题的解时,首先检查是否已经保存过此解,如果已经保存,则直接返回保存的值,否则,按照通常方式计算这个子问题。
自底向上法:
需要恰当定义问题的规模,使得子问题的求解都只依赖于更小的子问题的求解。因而可以将子问题按照规模排序,按照从小到大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它(也是第一次遇到它)时,它的所有前提子问题都已求解完成
适合应用动态规划方法求解的最优化问题应该具备的两个要素:
- 最优子结构
- 重叠子问题
最优子结构
原问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解
DP求解最优化问题的第一步就是刻画最优解的结构。如果一个问题的最优解包含其子问题的最优解,则称此问题具有最优子结构性质。使用DP时,会用子问题的最优解来构造原问题的最优解
寻找最优子结构的通用模式:
- 做出一个选择,作出的这次选择可能会产生一个或多个待解的子问题
- 对于一个给定问题,在其可能的第一步选择中,假定已经知道哪种选择会得到最优解。现在并不关心选择具体是如何得到的,只是假定已经知道了这种选择
- 确定选择会产生哪些子问题,以及如何最好的刻画这些子问题空间
- 利用“剪切-粘贴”(cut-and-paste)方法证明:作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解
证明方法:反证法:假定子问题的解不是其自身的最优解,那么我们就可以从原问题的解中cut掉这些非最优解,将最优解paste进去,从而得到原问题的一个更优的解,这与最初的解时原问题最优解的前提假设矛盾。如果原问题的最优解包含多个子问题,通常它们很相似,我们可以将针对一个子问题的cut-and-paste论证方法稍加修改用于其他子问题
刻画子问题空间的好经验:保持子问题空间尽可能简单,只在必要时才扩展它
当子问题空间有效时,不必尝试更一般性(从而也更大)的子问题空间
对于不同问题领域,最优子结构的不同体现在两个方面
- 原问题的最优解中涉及多少个子问题
- 在确定最优解使用那些子问题时,我们需要考察多少种选择
在动态规划中,我们通常自底向上地使用最优子结构。也就是说,首先求得子问题的最优解,然后求原问题的最优解。在求解原问题过程中,我们需要在涉及的子问题中作出选择,选择能得到原问题最优解的子问题。原问题最优化的代价通常就是子问题最优化代价再加上由此次划分所产生的代价
贪心算法与动态规划
最优子结构性质也适用于贪心策略
贪心与DP最大的不同在于:
DP首先寻找问题的最优解,然后在其中进行选择
贪心首先作出一次贪心选择–在当时(局部)看来是最优的选择–然后求解选出的子问题,而不必费心求解所有可能相关的子问题
重叠子问题
递归算法反复求解相同的子问题
递归算法对每个子问题,当在递归树中遇到它的时候,都需要重新计算一次
DP对于每个子问题求解一次,将其存放在一个表中,当再次需要这个子问题时直接查表,每次查表的时间代价为常量时间