算法设计与分析(第二版) 第3章

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
c[i 1, w]}, i 0 且 wi w
(3.3)
(3) 计算背包问题最优解的值。基于上述计算c[i, w] 的递归方程,我们很容易地写出一个递归算法RECURKNAP,计算背包容量为W时n个物品的最优解值。然而, 这个算法为指数时间复杂度O(2n),并不比穷举法好。
这个递归过程的初始调用为RECUR-KNAP(W)。 我们不是直接计算递归方程的解,而是利用一个表格, 以自底向上的方式计算最优解值。过程KNAPSACK-DP以物 品权值〈w1,w2,…,wn〉,物品价值〈v1,v2,…,vn〉,物品 个数n,背包容量W作为输入,并将c[i, w]的值存储在辅 助表c[0..n, 0..W]中,以行为主序从左到右计算表c中的元 素。同时维持辅助表s[1..n, 1..W],以简化最优解的构造。 为简单起见,我们只将物品个数及背包容量在参数表中列出。
图 3-1 计算斐波那契数算法的递归结构(n=7)
图3-1表明,为了计算F7,递归算法进行了多次重叠子 问题的递归调用, 而这些重叠子问题导致了指数级的算法。 在这个例子中,在第二次调用中忽略了前次调用所做的计算。
计算F6=8的递归调用如图3-2所示。树中每一个结点代表所 计算的斐波那契数。由于多重递归,导致了大量重复计算。
自顶向下的动态规划(top-down dynamic programming)方 法是更简单的一种技术,它执行递归函数的运行时间与自底 向上的动态规划的运行时间相同,有时更少。我们跟踪递归 程序,存储它计算的每一个值,并检查计算的值,以避免重 复计算。这种自顶向下的技术也称为备忘录(memoization)方 法。
对于更复杂的例子,考虑0-1背包问题(knapsack
problem)。某商店有n个物品,第i个物品价值为vi,重量(或 称权值)为wi,其中vi和wi为非负数。背包的容量为W,W为 一非负数。目标是如何选择装入背包的物品,使装入背包的
物品总价值最大。可将这个问题形式描述如下:
max vi xi
(3.1)
9
else c[i, w] c[i1, w]
10
else c[i, w] c[i1, w]
由算法KNAPSACK-DP的嵌套循环结构可得,算法的运
算法是非常低效的。实际上,计算Fn的递归调用次数恰好为
Fn+1。但是Fn约为jn,其中j≈1.618, 是黄金分割比例。因此,
Fn=O(1.618n),这是一个指数时间(exponential-time)级的算法。 图3-1是利用递归算法计算斐波那契数F7时的递归结构,由 此可见,重复计算的子问题数导致了指数级的复杂度。
利用自顶向下的动态规划设计策略,我们可以将计算斐 波那契数的递归算法(例2-1)变成以下的C语言函数:
int fibarr[1000]={0}; int MEMOIZED-F(int n)
{ int t; if( fibarr[n] !=0 return fibarr[n]; if (n==0) t=0; if (n==1) t=1; if (n>1) t=MEMOIZED-F(n-1)+MEMOIZED-F(n
KNAPSACK-DP(n, W)
1 for w 0 to W
2
do c[0, w] 0
3 for i 1 to n
4
do c[i, 0] 0
5
for w 1 to W
6
do if w[i] w
7
then if v[i] + c[i1, w w[i]] > c[i1, w]
8
then c[i, w] v[i] + c[i1, ww[i]]
定义问题最优解的开销。设c[i, w]表示背包容量为w时,i
个物品导致的最优解的总价值。显然,问题的最优价值为c
[n, W]。
由(1)中分析可得,c[i, w] 递归定义如下:
0
c[i,
w]
c[i 1, w]
max{ c[i 1, w wi ] vi ,
,i 0 或 w 0 ,wi w
利用自底向上的动态规划设计策略,我们可以将计算斐 波那契数的递归算法(例2Байду номын сангаас1)变成以下的C语言函数:
int F(int n) { int f, f1, f2, k, t; if (n<2) return n; else { f1=f2=1; for (k=2; k<n; k++) { f=f1+f2; f2=f1; f1=f;} } return f; }
-2); return fibarr[n]=t;
} 图3-3以n=7为例,说明了MEMOIZED-F的运行过程。
图 3-3 计算斐波那契数的自顶向下的动态规划示例(n=7)
算法将数组fibarr初始化为0,算法执行完成后,数组 fibarr中的值如图3-4所示。
图3-4 数组fibarr中的值
3.2 0-1背包问题
第3章 动态规划
3.1 用表代替递归 3.2 0-1背包问题 3.3 矩阵链乘问题 3.4 动态规划的基本元素 3.5 备忘录方法 3.6 装配线调度问题 3.7 最长公共子序列 3.8 最优二分检索树 3.9 凸多边形最优三角剖分 习题
3.1 用表代替递归
从第2章的例2.1中我们可以看到,计算斐波那契的递归
图3-2 计算F6时的递归调用
相应地,如果我们用一个数组作为数据结构,将计算的 前n个数存储在数组中,就可以用线性时间计算斐波那契数 Fn:
F[0] 0;F[1] 1;
for i 2 to n
do F[i] F[i 1] + F[i2] 计算结果Fn呈指数级增长,但是所需数组规模较小。例如, F45=1 836 311 903是32位整数所能表示的最大斐波那契数, 因此,定义大小为46的数组即可。
1in
wi xi W, xi {0,1}
1in
(3.2)
图3-5给出了问题的三种可行解。 图 3-5 0-1背包问题示例
(1) 刻画0-1背包问题最优解的结构。我们可以将背包问
题的求解过程看做是进行一系列的决策过程,即决定哪些物
品应该放入背包,哪些物品不放入背包。
(2) 递归定义最优解的值。根据子问题的最优解递归地
相关文档
最新文档