目录
概念
programming:表格法
同分治算法一样,是通过组合子问题的解来解决原问题。
动态规划算法通常用于求解最优化问题。
应用于子问题重叠的情况
步骤
-
- 分析最优解的性质,并刻画其结构特征
-
- 递归地定义最优解的值
-
- 计算最优解的值,通常采用自底向上的方法
-
- 利用计算出的信息构造一个最优解
最优解的结构特征
当完成首次切割后,我们将两段钢条看作两个独立的切割问题实。通过组合两个相关子问题的最优解,并在所有可能的两段切割中选取组合收益最大的,构成原问题的最优解。
例子
1.钢条切割
问题描述: 给定1个长度为n的钢条和钢条切割价格条目,求将钢条切割为若干 小段后,得到的最大收益。
长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
价格 | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
-
长度为n英寸
-
销售收益
-
假设最优解切割为k段,最优切割方案为
- 将钢条切割为k段,最大收益为
- 最优收益对应的最优切割方案
更一般的,对于,我们可以用更短的钢条最优收益来描述
指不分割的方案 钢条切割问题满足最优子结构
另一种相似但是更为简单的方案:i部分不分割,剩下的n-i部分进行分割,即问题分解的方式为:将长度为n的钢条分解为左边开始一段不分割以及剩余部分继续分解的结果,公式描述为:
CUT-ROD(p,n)
if n=0
return 0
else
q = -∞
for j=1 to n
q = max{q, p[j]+CUT-ROD(p,n-j)}
return q
下面展示如何用动态规划解决此问题
- 1.带备忘的自顶向下法
仍然按照自然的递归形式编写过程,使用散列表会这数组保存已求解的子问题
MEMOIZED-CUT-ROD(p,n)
let r[0..n] be a new Array;
for i=0 to n
r[i]=-∞;
//将辅助函数r[n]的值初始化为-∞
return MEMOIZED-CUT-ROD-AUX(p,n,r)
//上述CUT-ROD的变体,提供备忘机制
MEMOIZED-CUT-ROD-AUX(p,n,r)
//已知直接返回
if(r[n]>=0)
return r[n]
if(n==0)
q = 0
else
q = -∞
for j=1 to n
q = max{q, p[j]+MEMOIZED-CUT-ROD-AUX(p,n-j,r)}
//将q存入
r[n] = q
return q
- 2.自底向上法
按照问题的递归结构,自底向上求解,将子问题按照规模排序,再由小到大解决
BOTTOM-UP-CUT-ROD(p,n)
let r[0..n] be a new Array;
r[0] = 0
for j=1 to n
q = -∞
for i=1 to j
q = max{q, p[i]+r[j-i]}
r[j] = q
return r[n]
- 分析自顶向上
第一行创建一个新数组保存子问题的解,第二行将数组第一个值初始化为0
let r[0..n] be a new Array;
r[0] = 0
3-6行升序求解从1到n的规模为j的子问题
for j=1 to n
q = -∞
for i=1 to j
7行解决子问题,不必调用递归,直接访问r[j-i],即以保存的子问题的解
q = max{q, p[i]+r[j-i]}
8行将子问题的解存入数组,9行返回最优解
两种方法具有相同的渐进运行时间,
自底向上 嵌套的两层for循环
自顶向下 递归调用, 使用聚合分析,其中
for j=1 to n
q = max{q, p[j]+MEMOIZED-CUT-ROD-AUX(p,n-j,r)}
对每个子问题只求解一次,求解规模为 = O()
自顶向下递归树的简化版/自顶向下的子问题图
n=4 这是一个有向图,每个顶点唯一地对应一个子问题,标号代表子问题的规模
重构解
上述算法能返回最终的最优收益值,但并没有返回解本身。下面我们拓展动态规划算法,使其返回最优收益值的同时,返回一个最优解。
EXTENDED-BOTTOM-UP-CUT-ROD(p,n)
let r[0..n] and s[0..n] be a new Array;
r[0]=0
for j=1 to n
q = -∞
for i=1 to j
if q < p[i]+r[j-i]
q = p[i]+r[j-i]
s[j] = i
r[j] = q
return r and s
2.矩阵链乘法
矩阵乘法满足结合律,但乘法顺序不固定,所以需要求解最优乘法顺序,使得乘法次数最少