分治法:将问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来
动态规划:子问题重叠;通常用于求解最优化问题
分治法会做许多重复工作,反复求解公共子问题;而动态规划则对每个子问题只求解一次并保存下来
步骤:
1) 刻画一个最优解的结构特征
2) 递归地定义最优解的值
3) 计算最优解的值,通常采用自底向上的方法
4) 利用计算出的信息构造一个最优解【如果只是求最优解的值则不需要这一步】
最优子结构性质:问题的最优解由相关子问题的最优解组合而成,且这些子问题能独立求解
钢条切割问题:一根长为n英寸的钢条,其对应长度i有一个售价pi,求如何切割使得售价最高。
设最佳收益为rn,则
1. rn = max{pn, r1 + rn-1, r2 + rn-2, ..., rn-1 + r1}
从而钢条切割问题满足最优子结构性质
2. 也可以降钢条从左边切割下长度为i的一段,对右边剩下的长度为n-i的一段继续进行切割,对左边的一段则不再进行切割
rn = max(pi + rn-i) i = 1, ..., n
CUT-ROD(p, n)
if n == 0
return 0
q = inf
for i=1 to n
q = max(q, p[i] + CUT-ROD(p, n-i))
return q
性能差,随着n每次增大1,程序运行时间增长1倍;因为其反复求解重复的子问题
3. 根据上面的方法,每次将子问题的解保存下来,不必重新计算;典型的空间换时间
1) 带备忘的自顶向下法,过程保存每个子问题的解,当需要一个子问题的解时,先检查是否已经保存过此解
2) 自底向上法,需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解
BOTTOM-UP-CUT-ROD(p, n)
let r[0..n] be a new array
r[0] = 0
for j=1 to n
q = inf
for i=1 to j
q = max(q, p[i] + r[j-i])
r[j] = q
return r[n]
重构解,上面得到的只是最优解的收益值,而没有得到最优解本身(即切割方案)
下面的扩展,s保存了解规模为j的子问题时,第一段钢条的最优切割方案s[j];得到s[j]将其打印出来即可【PP209】
EXTEND-BOTTOM-UP-CUT-ROD(p, n)
let r[0..n], s[0..n] be a new array
r[0] = 0
for j=1 to n
q = inf
for i=1 to j
if q < p[i] + r[j-1]
q = p[i] + r[j-i]
s[j] = i
r[j] = q
return r and s
PRINT-CUT-ROD-SOLUTION(p, n)
(r, s) = EXTEND-BOTTOM-UP-CUT-ROD(p, n)
while n > 0
print s[n]
n = n - s[n]