背包实验报告.docx
《背包实验报告.docx》由会员分享,可在线阅读,更多相关《背包实验报告.docx(10页珍藏版)》请在冰豆网上搜索。
算法设计与分析实验报告
0_1背包
一.问题描述
假设有n件物品,每件物品有各自的重量W1,W2,……,Wn和
与之对应的价值V1,V2,……,Vn。
设背包的容量为c,在不超过背包容量的前提下,求出获得最大价值总和的方案。
(0-1背包的情况下物品不可分割,只能选择放入,或者不放入背包中)。
二.求解思路
1.贪心策略
问题开始阶段,将所有物品按价值从高到低排列,每一次往背包
里放入不超过背包容量的价值最大的物品,直到没有物品可放入为止。
但事实证明,由于物品的不可分割性,0-1背包并不适合贪心策略。
例:
假设背包的容量为50,共有三件物品(重量,价值):
(10,60),(20,100),(30,120)。
若使用贪心策略,则会选择一个(30,120)和一个(20,100)。
得到的价值总和是220。
而稍加计算便可知选取两个(20,100)和一个(10,60)可以得到更大的价值总和260。
因此贪心策略不能给出0-1背包的最优解。
后话:
即使是普通背包问题(物品可分割),每次选择价值最大的物品也不能得到最优解。
正确的贪心策略应是:
每次选择单位重量下价值最大的物品。
由于本次实验主要讨论的是0-1背包问题,这里就不给出该贪心策略的证明。
2.动态规划
(1)证明0-1背包问题具有最优子结构性质:
假设(x1,x2,……,xn)是容量为c的背包的一组最优解,其中xi的取值为0或1,表示是否放入背包中。
则必有(x2,x3,……,xn)为如下子问题的一组最优解:
sum{xi*wi}(2<=i<=n)<=c-x1*w1
利用反证法证明,假设(y1,y2,……,yn)是该子问题的一组最优解而(x2,x3,……,xn)不是。
则
sum{yi*vi}>sum{xi*vi}(2<=i<=n)
那么就可得到:
x1*v1+sum{yi*vi}>x1*v1+sum{xi*vi}(2<=i<=n)
则(x1,y2,……,yn)是原问题的最优解,而(x1,x2,……,xn)不是,与假设矛盾。
因此0-1背包具有最优子结构性质。
(2)状态转移方程
0i=0orj=0
m[i][j]=m[i-1][j]jmax{m[i-1][j-w[i]]+v[i],m[i-1][j]}j>=wi
证明:
m[i][j]表示在物品数为i,背包容量为j的情况下所得到的最大价值总和。
当物品数为0或背包容量为0的时候,最大价值自然为0;当物品数量增加到第i个的时候,若背包容量j比wi小,则无法装入该物品,因此物品i并未起到作用,相当于没有物品i,则m[i][j]=m[i-1][j];若背包容量j比wi大,则要比较加入物品i和不加入物品i这两种情况下哪种方案的价值总和最大,即
m[i][j]=max{m[i-1][j-w[i]]+v[i],m[i-1][j]}.
例:
背包容量为10
给出5个物品:
(2,6),(2,3),(6,5),(5,4),(4,6)
则得到m数组为:
3.上述算法改进
细看上述数组,仍存在许多多余的计算。
比如当存在i个物品时,若j=w1(假设为m(mj且k进一步分析,对于数组中的每一行来说,m[i][j]的值为一个递增的阶梯函数,则满足j=w条件的m[i][j]就是一个跳跃点,即价值总和会发生改变的点。
举个例子,仍使用上述的数据,得到的m数组如下图,其中红色点为跳跃点:
W
0
1
2
3
4
5
6
7
8
9
10
w=2,v=6
0
0
6
6
6
6
6
6
6
6
6
w=2,v=3
0
0
6
6
9
9
9
9
9
9
9
w=6,v=5
0
0
6
6
9
9
9
9
11
11
14
w=5,v=4
0
0
6
6
9
9
9
10
11
13
14
w=4,v=6
0
0
6
6
9
9
9
12
15
15
15
那么一个改进后的解题思路就出来了:
倘若把每个图的跳跃点的横纵坐标记录下来,就相当于把整个函数的信息储存下来。
对于表格中的每一行来说我们只要把每次跳变时的价值和和总重量记录下来,就可以把整行的数据表示出来了
于是,我们可以建立这样一个集合p[i]:
p[i]中储存第i行所有的跳变点
p[i]中的元素为(w,v),w为总重量,v为价值和
这时候再来看看原来的状态转移方程:
0i=0orj=0
m[i][j]=m[i-1][j]jmax{m[i-1][j-w[i]]+v[i],m[i-1][j]}j>=wi
当j当j>Wi时,如果有新的跳跃点出现,一定是在j任意(w,v)∈p[i](w+wi,v+vi)∈q[i]
那么我们在求p[i+1]的时候就可以通过p[i]∪q[i],再去除一些不可能点来求得。
这些不可能点可以用两个集合表示:
集合A:
w>c(背包容量)的点,因为这违反了题目要求
集合B:
在p[i]和q[i]的并集中,如果存在(a,b)和(c,d)是p[i]Èq[i]中的2个跳跃点,而且c³a且d
因为在求最大值的过程中,如果的d
那么p[i+1]=p[i]∪q[i]-A-B;
最后求得的p[i]的最后一个元素就会是在i个物品的情况下,所得到的最大价值总和。
三.编程实现
1.编译环境:
Dev-C++
2.代码实现:
使用一个结构类型来代表物品,有重量和价值两个成员。
(1)动态规划算法
(2)改进算法
用一个二维的指针数组goods**p来表示集合p[i],当i取不同值时,集合里的元素对应着数组里的每一行。
求集合p[i]:
求q[i]:
求p[i+1]:
其中集合A为:
集合B为:
举个例子,以上述数据得到的p数组为:
得到了最大价值总和后,则可以利用p数组来找出最优解的放置方案:
初设i=n,比较p[i]与p[i-1]的最后一个元素,如果不同,则第i
个物品一定被选上了(因为此时多了一个跳跃点,则证明是多加了物
品i的缘故);如果相同,则i--,直到找到符合条件的i为止。
这样,
就得到了第一个解。
假设最大价值为re,从找到的第i行出发,使re减去第i个物品的价值,寻找所得结果第一次出现的行。
则该行所代表的物品一定被选上了(原因同上)。
再将所得结果减去该行代表的物品的价值,重复以上步骤,直至re为0为止。
其中getEnd()函数的作用是找到p[i]的最后一个元素,返回其下标。
上述例子的解为:
3.测试数据
本实验提供了30组数据,物品重量从1到3000不等,背包容量
从1到10000不等,物品数从5到50不等。
由于物品重量和背包容量都是由随机数产生,则得到的最大价值总和也可能出现0的情况。
当背包容量较小时,两种算法的计算时间差不多,但当c>2n时,算法一需要Ω(n2n),而算法二需要O(min{nc,2n}),可节约时间。
四.实验总结
使用动态规划算法一的时候,需开辟一个n*c的数组,当背包容量比较大的时候会耗费空间,且算法一存在许多不必要的计算,也浪费了时间。
使得其时间复杂度为Ω(n2n)。
算法二对算法一进行了优化,通过寻找跳跃点的方式减少了不必要的计算,节约了时间,其时间复杂度为O(min{nc,2n})。
但寻找跳跃点的过程时略显麻烦,也需要开辟一个二维数组,在空间上并没有很大改进。