【0/1背包问题】
在0/1背包问题中,需对容量为c 的背包进行装载。从n 个物品中选取装入背包的物品,每件物品i 的重量为wi ,价值为pi 。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即p1*x1+p2*x1+...+pi*xi(其1<=i<=n,x取0或1,取1表示选取物品i) 取得最大值。
【输入文件】
第一行一个数c,为背包容量。
第二行一个数n,为物品数量
第三行n个数,以空格间隔,为n个物品的重量
第四行n个数,以空格间隔,为n个物品的价值
【输出文件】
能取得的最大价值。
【分析】
初看这类问题,第一个想到的会是贪心,但是贪心法却无法保证一定能得到最优解,看以下实例:
贪心准则1:从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入(假设有足够容量),然后是下一个价值最大的物品,如此继续下去。这种策略不能保证得到最优解。例如,考虑n=2, w=[100,10,10], p =[20,15,15], c =105。当利用价值贪婪准则时,获得的解为x= [1,0,0],这种方案的总价值为20。而最优解为[0,1,1],其总价值为30。
贪心准则2:从剩下的物品中选择可装入背包的重量最小的物品。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑n= 2 ,w=[10,20], p=[5,100], c= 2 5。当利用重量贪婪策略时,获得的解为x =[1,0], 比最优解[ 0 , 1 ]要差。
贪心准则3:价值密度pi /wi 贪婪算法,这种选择准则为:从剩余物品中选择可 装入包的pi /wi 值最大的物品,但是这种策略也不能保证得到最优解。利用此策略解 n=3 ,w=[20,15,15], p=[40,25,25], c=30 时的得到的就不是最优解。
由此我们知道无法使用贪心算法来解此类问题。我们采用如下思路:
在该问题中需要决定x1 .. xn的值。假设按i = 1,2,...,n 的次序来确定xi 的值。如果置x1 = 0,则问题转变为相对于其余物品(即物品2,3,.,n),背包容量仍为c 的背包问题。若置x1 = 1,问题就变为关于最大背包容量为c-w1 的问题。现设r={c,c-w1} 为剩余的背包容量。在第一次决策之后,剩下的问题便是考虑背包容量为r 时的决策。不管x1 是0或是1,[x2 ,.,xn ] 必须是第一次决策之后的一个最优方案。也就是说在此问题中,最优决策序列由最优决策子序列组成。
假设f (i,j) 表示剩余容量为j,剩余物品为i,i + 1,...,n 时的最优解的值,即:利用最优序列由最优子序列构成的结论,可得到f 的递归式为:
当j≥wi时: f(i,j)=max{f(i+1,j),f(i+1,j-wi)+pi}
当0≤j<wi时:f(i,j)=f(i+1,j)
这是一个递归的算法,其时间效率较低,为指数级。
考虑用动态规划的方法来解决:
阶段:在前i件物品中,选取若干件物品放入背包中;
状态:在前i件物品中,选取若干件物品放入所剩空间为c的背包中的所能获得的最大价值;
决策:第i件物品放或者不放;
由此可以写出动态转移方程:
用f[i,j]表示在前 i 件物品中选择若干件放在所剩空间为 j 的背包里所能获得的最大价值
f[i,j]=max{f[i-1,j-wi]+pi (j>=wi), f[i-1,j]}
这样,就可以自底向上地得出在前n件物品中取出若干件放进背包能获得的最大价值,也就是f[n,c]
算法框架如下:
for i:=0 to c do {i=0也就是没有物品时清零}
f[0,i]:=0;
for i:=1 to n do {枚举n件物品}
for j:=0 to c do {枚举所有的装入情况}
begin
f[i,j]:=f[i-1,j]; {先让本次装入结果等于上次结果}
if (j>=w[i]) and (f[i-1,j-w[i]]+p[i]>f[i,j]) {如果能装第i件物品}
then f[i,j]:=f[i-1,j-w[i]]+p[i]; {且装入后价值变大则装入}
end;
writeln(f[n,c]);
为了进一步说明算法的执行原理,下面给出一个实例:
【输入文件】
10
4
5 1 4 3
40 10 25 30
【输出结果】下面列出所有的f[i,j]
0 0 0 0 40 40 40 40 40 40
10 10 10 10 40 50 50 50 50 50
10 10 10 25 40 50 50 50 65 75
10 10 30 40 40 50 55 70 80 80
从以上的数据中我们可以清晰地看到每一次的枚举结果,每一行都表示一个阶段。