动态规划(3)——算法导论(18)
1. 写在前面
我们已经用动态规划的方法解决了两个问题,但可能还是弄不清应该在何时使用动态规划。本篇,我们对动态规划方法做一个总结,重点关注适合应用动态规划方法求解的最优化问题应该具备的两个要素:最优子结构和子问题重叠。
2. 最优子结构
用动态规划方法求解最优化问题的第一步就是刻画最优解的结构。如之前的两个问题
如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。
当然,具有最优子结构性质只是判断一个问题是否适用动态规划方法的一个很好的线索,它也可能适用于贪心策略。
回顾我们之前的两个问题。在钢条切割问题中,我们观察到,长度为n的钢条的最优切割方案是由第一次切割后(如果最优方案需要切割)得到的两端钢条的最优切割方案组成的;在矩阵乘法链问题中,我们同样看到,
我们发现,在发掘最优子结构性质的过程中,实际遵循了如下的通用模式:
-
证明问题最优解的第一个组成部分是做出了一个选择。例如,选择钢条第一次切割的位置,选择矩阵链的划分位置。做出这次选择会产生一个或多个待解的子问题。
-
对于一个给定的问题,在其可能的第一步选择中,我们假定已经知道哪种选择才会得到最优解。但是我们并不关心这种选择具体是如何得到的,只是假定已经知道了这种选择。
-
给定可获得最优解的选择后,你确定这次选择会产生哪些子问题,以及如何最好的刻画子问题空间。
-
利用剪切—粘贴(cut-and-parse)技术证明:作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解。利用反证法可以证明:假设子问题的解不是其自身的最优解,那么我们就可以从原问题的解中“剪切”掉这些非最优解,将最优解“粘贴”进去,从而得到原问题一个更优的解,这与最初的解是原问题的最优解相矛盾。
一个刻画子问题空间的好经验是:保持子问题空间尽可能简单,但在必要时才扩展它。例如,在求解钢条切割问题时,子问题空间中包含的问题为:对每个i值,长度为i的钢条的最优切割问题。这个子问题空间已经很一般了。
对于动态规划问题,我们还需要注意的两个方面是:
- 原问题的最优解中,涉及多少个子问题;
- 在确定最优解使用哪些子问题时,我们需要考察多少种选择。
例如,在钢条切割问题中,长度为n的钢条的最优切割方案仅仅使用一个子问题(长度为n-i的钢条的最优切割问题),但我们必须考察i的n种不同的取值,来确定哪一个会产生最优解。
再比如,在矩阵链乘法问题中,最优解使用了两个子问题,需要考察j-i种情况。
之所以需要注意上述两个方面,是因为我们可以用来它来粗略的计算动态规划算法的时间效率。对于钢条切割问题,共有Θ(n)个子问题,每个子问题最多需要考察n种选择,因此运行时间为O(n2)。矩阵链乘法问题共有Θ(n2)个子问题,每个子问题最多需要考察n-1中选择,因此运行时间为O(n^3)。
3. 子问题重叠
适合用动态规划问题方法求解的最优化问题应该具备的第二个性质是子问题空间必须足够“小”,即问题的递归算法会反复地求解相同的子问题,而不是一直生成新的子问题。一般来说,不同的子问题的总数是输入规模的多项式函数为好。
如果递归算法反复求解相同的子问题,我们就称最优化问题具有折叠子问题(overlapping)性质。
动态规划算法通常是采用“备忘录”来利用该性质:对每个子问题求解一次,将解存入“备忘录”中;当再次需要这个子问题时直接查表,这样可保证再次求解相同问题时,只需要花费常量时间。