《算法导论》动态规划笔记
动态规划的方法是付出额外的内存空间来节省计算时间,是典型的时空权衡(time-memory trade-off)的例子。时间上的节省可能是非常巨大的,有可能将指数时间的解转化为多项式时间的解。
应用动态规划方法求解的优化问题应该具备的两个要素:最优子结构和子问题重叠。
最优子结构:
如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。
对于不同问题领域,最优子结构的不同体现在两个方面:
·原问题的最优解涉及多少个子问题
在确定最优解使用那些子问题时,我们需要考察多少种选择
最优子结构的性质:
问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
独立求解也就是同一个原问题的一个子问题的解不影响另一个子问题的解。
例:无权最长路径问题不满足子问题的独立性,因为在两个子问题中可能会重复利用某些顶点。
动态规划设计的一般步骤:
刻画一个最优解的结构特征
递归地定义最优解的值
计算最优解的值,通常采用自底向上的方法
利用计算出的信息构造一个最优解
动态规划有两种等价的实现方法:
带备忘的自顶向下法(top-down with memoization)。此方法按照自然的递归形式编写,但是过程中会保存每个子问题的解,通常存在一个数组或者散列表中。当需要一个子问题的解的时候,过程首先你检查是否已经保存过这个解。如果是,就直接返回此保存的值。
自底向上法(bottom-up method)。当求解某个子问题的时候,它所依赖的那些更小的子问题都已经求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它时,它的所有前提子问题都已经求解完成。
由于没有频繁地递归函数调用的开销,自底向上的方法比自顶向下法的时间开销可能会小一些。
例题以及伪代码实现:
钢条切割问题:给定一段长度为n英寸的规划钢条和一个价格表p,求切割钢条的方案,使销售收益r最大。
自顶向下版本
MEMOIZED-CUT-ROD(p,n)
let r[0..n] be a new array
for i=0 to n
r[i]=-oo
return MEMOIZED-CUT-ROD-AUX
MEMOIZED-CUT-ROD-AUX(p,n,r)
if r[n]>=0
return r[n]
if n==0
q=0
else q=-oo
for i=1 to n
q=max(q,p[i]+MEMOIZED-CUT-ROD-AUX(p,n-i,r))
r[n]=q
return q
自底而上版本
BOTTOM-UP-CUT-ROD(p,n)
let r[0..n] be a new array
r[0]=0
for j=1 to n
q=-oo
for i= 1 to j
q=max(q,p[i]+r[j-i])
r[j]=q
return r[n]
性能比较:
自底向上和自顶向下的算法具有相同的渐进运行时间,均为theta(n^2)。
自底向上动态规划算法是按“逆拓扑序”(reverse topological sort)来处理子问题图中的顶点。
《算法导论》贪心算法笔记
算法设计
1. 将最优化问题转化为这样的形式:对其作出一次选择后,只身下一个子问题需要求解。
2. 证明作出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的。
3. 证明作出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构。
贪心选择性质(greedy-choice property)
我们可以通过作出局部最优选择来构造全局最优解。
贪心算法与动态规划的不同之处:
在动态规划中,每个步骤都要进行一次选择,但是选择通常依赖于子问题的解。在贪心算法中,我们直接作出在当前问题中看来最优的选择,而不必考虑子问题的解。
例:
0-1背包问题不具贪心选择性质
分数背包问题具有贪心选择性质