本文总结了几种常见动态规划算法的分析策略,但不做案例的具体分析,阅读前最好对这几种算法有一定基础了解。
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。
与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。
(1) 确定最优解结构
(2) 递归定义最优解的值
(3) 自底向上计算最优解的值
(4) 重构最优解
分析:首先确定最优解结构,分析问题可知大致分为两种情况:
从第一个站出站(j=1)和从第j个站出站(j>=2)。
当j=1:货物上线后只经过一个站,f1[j]=e1+a1,1
当j>=2,又可分为跳线和不跳线两种情况:
不跳线:f1[j]=f1[j-1]+a1,j
跳线:当货物从f2跳到f1,有一个跳转时间t2,j-1,则:f1[j]=f2[j-1]+t2,j-1+a1,j
由对称关系,f2[j]可同理得出,最后:
传输完后,加上自动下线时间x1,x2,总装配时间:
最后采用自底向上的方法求解算法并递归的输出最优解的值。
分析:若只有一个矩阵,无最优解。若大于一个矩阵:对于A1,A2,…,An,我们得在和之间加上一个括号使得分开的两个子矩阵链乘积最小,再分别对两个子问题找到最优的划分结果:
设m[i,j] 为计算矩阵链Ai..j 的乘积所需的最少标量乘法次数。
若:i=j,不需任何计算,即m[i,j]=0,否则:
则最终公式为:
在计算时,采用了自底向上的方法来求解最优解,在求解过程中用一个辅助的数组S[1….n-1,2….n]来记录最优值m[i,j]对应的分割点K,这样能够构造出最优解。最后,借助辅助数组递归的输出最优解的值。
分析:可分为最后一个元素相同和不相同两种情况:
最后一个元素相同:求X[1…m-1]和Y[1…n-1]两个子序列的最长公共子序列。
最后一个元素不同:求X[1…m-1]和Y[1…n]或者X[1…m-1]和Y[1…n]两个子序列的最长公共子序列。
令C[i,j]为的LCS的长度,如果i=0或者j=0则LCS=0,则根据LCS的最优子结构特征我们可以构造出:
根据递归式,我们能写出一个递归算法来计算最长公共子序列,由于LCS的子问题过多,所以我们采用自底向上的计算。
在这个过程中,我们需要借组一个数组b[i,j]来记录最优解得构造过程,利用b[i,j]所记录的元素来输出最优解。
分析:求给定的n个整数(可能但不全为负)a1,a2,…,an, 的i跟j,使 ai 到 aj 的和最大。我们定义b[j]=max(sum(i:j)),
为从i到j子段的最大子段和。我们比较b[j-1]+a[j]和a[j]的大小,如果b[j-1]<0,显然b[j-1]不是最大子段,此时b[j]=a[j]。反之,我们令b[j-1] + a[j] = b[j],找出最大的子段和。
则:b[j]=max( b[j-1]+a[j], a[j] ), 1<=j<=n
由上面的递归公式我们可以写出一个自底向上的递归算法,在算法中我们借助一个变量sum来记录过程中的最大子段和,若此时的b[j]>sum,更新sum中的值,反之,继续求解。直到程序进行完毕,输入sum中的最大子段和。
分析:分数背包问题可以采用贪心策略解决,但我们在求解0-1背包问题时,我们只能采用动态规划策略。
同样地:首先构造最优子结构。令c[i,j]表示利用前i个物品装容量为j的背包所能获得的最大价值,可分两种情况:
含物品n:去掉第n个物品,用W-wn容量的背包装物品1,2,…,n-1:c[i,j]=c[i-1,j-wi]+vi
不含物品n:用W容量背包装物品1,2,…,n-1:c[i,j]=c[i-1,j]
当然,没有物品或没有容量,c[i,j]=0
则总的递归式:
有上述递归方程,就可写出相应递归算法,但该递归算法复杂度太高,可用V[0..n,0..W]来保存子问题(i,j)的最大值。b[1..n,1..W]用来保存所做出的最优选择,以便构造最优解。在计算最优解的时候,保存所做出的最优决策,便可得到最优解。
一般动态规划问题都是基于此策略。在用这种方法时一般需要恰当的定义子问题的规模,使得任何子问题都只依赖于更小的子问题的求解。我们可以将问题的规模按照由大到小排列依次求解。每个子问题都只求解一次。
动态规划有一个性质为子问题重叠性质,就是对于子问题在递归过程中不断求解,虽然问题规模很小,但是求解次数会非常多,造成程序运行非常慢。在使用自顶向下的求解过程中,我们一般要设计一个备忘录,在递归求解过程中对于已经求解过的问题保存在备忘录中,当下次要使用时直接拿出来,不用再次求解。
自顶向下只需要求解问题需要的解,不需要对所有子问题都去求解。但是它需要额外的递归开销。自底向上必须对所有子问题进行求解但是可有效减少计算时间和所需的存储空间。
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。解决动态规划问题的关键是找到最最优子结构并定义出递归式,根据经验,通常会分为若干种情况分开讨论,尤其注意容易遗漏的特殊情况(0、1、相等…)。在求解计算时,如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。