01背包问题的多种解法Word下载.docx

上传人:b****5 文档编号:19501416 上传时间:2023-01-07 格式:DOCX 页数:13 大小:44.90KB
下载 相关 举报
01背包问题的多种解法Word下载.docx_第1页
第1页 / 共13页
01背包问题的多种解法Word下载.docx_第2页
第2页 / 共13页
01背包问题的多种解法Word下载.docx_第3页
第3页 / 共13页
01背包问题的多种解法Word下载.docx_第4页
第4页 / 共13页
01背包问题的多种解法Word下载.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

01背包问题的多种解法Word下载.docx

《01背包问题的多种解法Word下载.docx》由会员分享,可在线阅读,更多相关《01背包问题的多种解法Word下载.docx(13页珍藏版)》请在冰豆网上搜索。

01背包问题的多种解法Word下载.docx

i2i2i2

v1x1viyiv1x1vixivixi,这说明(x1,y2,y3,,yn)是所给的0-1背包问

i2i2i1

题比(x1,x2,x3,,xn)更优的解,从而与假设矛盾。

穷举法:

用穷举法解决0-1背包问题,需要考虑给定n个物品集合的所有子集,找出所有可能的子集(总重量不超过背包重量的子集),计算每个子集的总重量,然后在他们中找到价值最大的子集。

由于程序过于简单,在这里就不再给出,用实例说明求解过程。

下面给出了4个物品和一个容量为10的背包,下图就是用穷举法求解0-1背包问题的过程。

背包

W1=7

V1=12

物品1

W2=3

V2=12

物品2

W3=4

V3=40

物品3

W4=5

V4=25

物品4

a)四个物品和一个容量为10的背包

序号

子集

总重量

总价值

1

空集

9

{2,3}

7

52

2

{1}

42

10

{2,4}

8

37

3

{2}

12

11

{3,4}

65

4

{3}

40

{1,2,3}

14

不可行

5

{4}

25

13

{1,2,4}

15

6

{1,2}

54

{1,3,4}

16

{1,3}

{2,3,4}

{1,4}

{1,2,3,4}

19

(b)用回溯法求解0-1背包问题的过程

递归法:

在利用递归法解决0-1背包问题时,我们可以先从第n个物品看起。

每次的递归调用都会判断两种情况:

(1)背包可以放下第n个物品,则x[n]=1,并继续递归调用物品重量为W-w[n],物品数目为n-1的递归函数,并返回此递归函数值与v[n]的和作为背包问题的最优解;

(2)背包放不下第n个物品,则x[n]=0,并继续递归调用背包容量为W,物品数目为n-1的递归函数,并返回此递归函数值最为背包问题的最优解。

递归调用的终结条件是背包的容量为0或物品的数量为0.此时就得到了0-1背包问题的最优解。

用递归法解0-1背包问题可以归结为下函数:

KnapSack(n1,m)没有选择物品n

KnapSack(n,m)

KnapSack(n1,mw[n])v[n]选择了物品n

第一个式子表示选择物品n后得到价值KnapSack(n1,mw[n])v[n]比不选择物品n情况下得到的价值KnapSack(n1,m)小,所以最终还是不选择物品n;

第二个式子刚好相反,选择物品n后的价值KnapSack(n1,mw[n])v[n]不小于不选择物品n情况下得到了价值KnapSack(n1,m),所以最终选择物品n。

在递归调用的过程中可以顺便求出所选择的物品。

下面是标记物品被选情况的数组

x[n]求解的具体函数表示:

0KnapSack(n,m)KnapSack(n1,m)

x[n]

1KnapSack(n,m)KnapSack(n1,mw[n])v[n]

在函数中,递归调用的主体函数为KnapSack,m表示背包的容量,n表示物品的数

量,x[n]表示是否选择了第n个物品(1—选,0—不选)。

每个物品的重量和价值信息分别存放在数组w[n]和v[n]中。

具体的代码见《递归法》文件夹。

贪心法:

0-1背包问题与背包问题类似,所不同的是在选择物品i(1in)装入背包时,可以选择一部分,而不一定要全部装入背包。

这两类问题都具有最优子结构性质,相当相似。

但是背包问题可以用贪心法求解,而0-1背包问题却不能用贪心法求解。

贪心法之所以得不到最优解,是由于物品不允许分割,因此,无法保证最终能将背包装满,部分闲置的背包容量使背包单位重量的价值降低了。

事实上,在考虑0-1背包问题时,应比较选择物品和不

选择物品所导致的方案,然后做出最优解。

由此导出了许多相互重叠的子问题,所以,0-1

背包问题可以用动态规划法得到最优解。

在这里就不再用贪心法解0-1背包问题了。

动态规划法分析:

0-1背包问题可以看作是寻找一个序列(x1,x2,x3,,xn),对任一个变量xi的判断

是决定xi=1还是xi=0.在判断完xi1之后,已经确定了(x1,x2,x3,,xi1),在判断xi

时,会有两种情况:

(1)背包容量不足以装入物品i,则xi=0,背包的价值不增加;

(2)背包的容量可以装下物品i,则xi=1,背包的价值增加vi。

这两种情况下背包的总价值的最大者应该是对xi判断后的价值。

令C(i,j)表示在前

i(1in)个物品中能够装入容量为j(1jW)的背包的物品的总价值,则可以得到如

下的动态规划函数:

C(i,0)C(0,j)0

(1)

C(i1,j)jwi

C(i,j)i

(2)max{C(i1,j),C(i1,jwi)vi}jwi

(1)说明:

把前面i个物品装入容量为0的背包和把0个物品装入容量为j的背包,

得到的价值均为0.式

(2)第一个式子说明:

如果第i个物品的重量大于背包的容量,则装

入第i个物品得到的最大价值和装入第i-1个物品得到的最大价值是相同的,即物品i不能装入背包中;

第二个式子说明:

如果第i个物品的重量小于背包的容量,则会有两种情况:

(1)如果把第i个物品装入背包,则背包中物品的价值就等于把前i-1个物品装入容量为

jwi的背包中的价值加上第i个物品的价值vi;

(2)如果第i个物品没有装入背包,则背包中物品的价值就是等于把前i-1个物品装入容量为j的背包中所取得的价值。

显然,取二者中价值较大者作为把前i个物品装入容量为j的背包中的最优解。

我们可以一步一步的解出我们所需要的解。

第一步,只装入第一个物品,确定在各种情况下背包能得到的最大价值;

第二步,只装入前两个物品,确定在各种情况下的背包能够得到的最大价值;

一次类推,到了第n步就得到我们所需要的最优解了。

最后,C(n,W)便是在容量为W的背包中装入n个物品时取得的最大价值。

为了确定装入背包的具体物品,从C(n,W)的值向前寻找,如果C(n,W)>

C(n1,W),说明第n个物品被装入了背包中,前n-1个物品被装入容量为Wwn的背包中;

否则,第n个物品没有装入背包中,

前n-1个物品被装入容量为W的背包中。

依此类推,直到确定第一个物品是否被装入背

包为止。

由此,我们可以得到如下的函数:

0C(i,j)C(i1,j)

1,jjwiC(i,j)C(i1,j)

根据动态规划函数,用一个(n1)(W1)的二维数组C存放中间变量,C[i][j]表

示把前i个物品装入容量为j的背包中获得的最大价值。

设物品的重量存放在数组w[n]中,价值存放在数组v[n]中,背包的容量为W,数组

C[n1][W1]存放迭代的结果,数组x[n]存放装入背包的物品,动态规划解0-1背包问

题的源代码在文件夹《动态规划法》中。

回溯法分析:

用回溯法解0_1背包问题时,会用到状态空间树。

在搜索状态空间树时,只要其左儿子

结点是一个可行结点,搜索就进入其左子树。

当右子树有可能包含最优解时才进入右子树

搜索,否则将右子树剪去。

设r是当前剩余物品价值总和;

cp是当前价值;

bestp是当前最优价值。

当cp+r≤bestp时,可剪去右子树。

计算右子树中解的上界可以用的方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。

由此得到的价值是右子树中解的上界,用此值来剪枝。

为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考察各物品即可。

在实现时,由MaxBoundary函数计算当前结点处的上界。

它是类Knap的私有成员。

Knap的其他成员记录了解空间树种的节点信息,以减少函数参数的传递以及递归调用时所需要的栈空间。

在解空间树的当前扩展结点处,仅当要进入右子树时才计算上界函数MaxBoundary,以判断是否可以将右子树减去。

进入左子树时不需要计算上界,因为其上界与父结点的上界相同。

在调用函数Knapsack之前,需要先将各物品依其单位重量价值从达到小排序。

为此目的,我们定义了类Objiect。

其中,运算符与通常的定义相反,其目的是为了方便调用已有的排序算法。

在通常情况下,排序算法将待排序元素从小到大排序。

在搜索状态空间树时,由函数Backtrack控制。

在函数中是利用递归调用的方法实现了空间树的搜索。

具体的代码见《回溯法》文件夹。

限界分支法:

在解0-1背包问题的优先队列式界限分支法中,活结点优先队列中结点元素N的优先级由该结点的上界函数MaxBoundary计算出的值uprofit给出。

该上界函数在0-1背包问题的回溯法总已经说明过了。

子集树中以结点N为根的子树中任一个结点的价值不超过N.profit。

因此我们用一个最大堆来实现活结点优先队列。

堆中元素类型为HeapNode,

其私有成员有uprofit,profit,weight,level,和ptr。

对于任意一个活结点N,N.weight是活结点N所相应的重量;

N.profit是N所相应的价值;

N.uprofit是结点N的价值上界,最大堆以这个值作为优先级。

子集空间树中结点类型为bbnode。

在分支限界法中用到的类Knap与0-1背包问题的回溯法中用到的类Knap很相似。

他们的区别是新的类中没有了成员变量bestp,而增加了新的成员bestx。

Bestx[i]=1,当且仅当最优解含有物品i。

在类Knap中有四个函数:

(1)上界函数MaxBoundary(),计算节点所对应价值的上界;

(2)函数AddLiveNode()是将一个新的活结点插入到子集树和优先队列中;

(3)函数MaxKnapsack()实施对子集树的优先队列式分支界限搜索。

其中假定物品依其单位价值从大到小已经排好序。

相应的排序过程会在算法的预处理部分完成。

算法中E是当前扩展结点;

cw是该结点的重量;

cp是该结点的价值;

up是价值上界。

算法的while循环不断扩展结点,直到子集树的一个叶结点成为扩展结点为止。

此时优先队列中所有活结点的价值上界均不超过该叶结点的价值。

因此该叶结点相应的解为问题的最优解。

在while循环内部,算法首先检查当前扩展结点的左儿子结点的可行性。

如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。

当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。

(4)函数Knapsack()完成对输入数据的处理。

其主要任务是将各物品依其单位重量价值从达到小排好序。

然后调用函数MaxKnapsack()完成对子集树的优先队列式分支限界搜索。

具体的实现代码在文件夹《分支限界法》中。

三、时空效率分析

对于一个有n个元素的集合,其子集数量为2n,所以,不论生成子集的算法效率有多高,穷举法都会导致一个O(2n)的算法。

在递归法的算法体中有一个if判断中出现了两次递归调用比较大小所以它们之间的递归关系式可以大体表示为:

T(n)2T(n1)C,其中T(n)表示递归法的时间复杂度,C是常数。

求解递归方程可以知道T(n)的量级为O(2n)。

所以递归法解0-1背包问题的时间复杂度为O(2n)。

递归法是耗费空间最多的算法,每次递归调用都需要压栈,导致栈的使用很频繁。

动态规划法:

由于函数Knapsack中有一个两重for循环,所以时间复杂度为O[(n+1)x(m+1)].空间复复杂度也是O[(n+1)x(m+1)],即O(nm).

回溯法:

由于计算上界的函数MaxBoundary需要O(n)时间,在最坏情况下有O(2n)个右儿子结点需要计算上界,所以解0-1背包问题的回溯法算法BackTrack所需要的计算时间为O(n2n).

在使用限界分治法时,就是使用更好的限界剪枝函数使得不必要的解被剔除,但是在最坏情况下的解仍然是和回溯法是相同的。

本算法中也是用到了计算上界的函数

MaxBoundary需要O(n)的时间,而且在最坏情况下有O(2n)个结点需要计算上界,所

以在最坏情况下的时间复杂度仍然为O(n2n)。

四、运行结果

 

递归法输出结果:

动态规划法输出结果:

回溯法输出结果:

分枝限界法输出结果:

五、分析输出结果

面测试的是每种算法在两种输入情况下得到的

0-1背包问题的解。

两种测试数据为:

第一组:

背包容量:

18;

物品数目

7;

每个物品重量为:

112

10;

每个物品价值为:

78

14。

第二组:

50;

10

24

93521

1819;

34

32

56

67

324556

4670。

但是可能是算法设计时的问

四种实现的算法中,只有回溯法没能够得到预期的最优解。

题,其实回溯法是穷举法的变形,肯定能够得到最优解的,这里是我设计函数的问题。

从递归法的输出可知,它的结果就是我们想要的最优解)。

从时间复杂度和空间复杂度分析可知,

动态规划法的时间复杂度是最小的,但是同时它的空间复杂度又是最大的。

这里就可以看出在设计算法的过程中要考虑它们的平衡问题。

在时间要求比较快的情况下,我们就可以选择

动态规划法;

在空间要求比较高时,我们就可以使用穷举法或是分枝限界法等其他改进的穷举法。

各种算法在解背包问题时的比较如下表所示:

算法名称

时间复杂度

优点

缺点

改进

穷举法

O(2n)

最优解

速度慢

剪枝

递归法

空间消耗大

用数组存

动态规划法

O(nm)

递归方程求解

贪心法

O(nlog2n)

不一定是最优解

速度快

可以作为启发

回溯法

O(n2n)

改进剪枝

分枝限界法

优化限界函数

从计算复杂性理论看,背包问题是NP完全问题。

半个多世纪以来,该问题一直是算法与复杂性研究的热门话题。

通过对0-1背包问题的算法研究可以看出,回溯法和分枝限界法等可以得到问题的最优解,可是计算时间太慢;

动态规划法也可以得到最优解,当m2n时,算法需要O(n2n)的计算时间,这与回溯法存在一样的缺点——计算速度慢;

采用贪心算法,虽然耗费上优于前者,但是不一定是最优解。

目前,以上几种方法中回溯法、动态规划法、贪心法都广泛地应用到不同的实际问题中,并在应用中不断地改进。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 医药卫生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1