码迷,mamicode.com
首页 > 其他好文 > 详细

趣题——三类背包问题

时间:2015-02-16 18:27:03      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:dp   背包   趣题   

引言

背包问题作为一个经典问题在动态规划中是很基础的一个部分,而以0-1背包问题为原题,衍生出来的各类题目千变万化,解法也不同。
本文以三道背包问题的变体着手讲解,希望能够做到触类旁通。


0-1背包

给定n件物品和一个背包。物品i的价值是Wi,体积为Vi,背包的容量为C。可以任意选择装入背包的物品,求装入背包中的物品最大总价值。(Vi,C均为正整数)

在选择装入背包的物品时,有这样一个隐性条件:对每件物品i来说,要么全都放入背包,要么不放。不能将物品i多次装入(有切仅有一个),也不能部分装入。因此该题为0-1背包问题。

在使用动态规划(DP)求解该题时,最重要的是找到转换方程。
W[i]:代表第i件物体的价值;
V[i]:代表第i件物体的体积;
令F[i][j]:代表在剩余空闲体积为j的条件下,处理第i个物体的情况。
其中有一个隐含的过程:即当考虑到第i个物体时,说明已经考虑完了第i-1,i-2,i-3,…,1个物品。即,不用再返回判断前面的物品情况。
因此整个转移方程应为:
若j < Vi,则F[i][j] = F[i-1][j].
否则,F[i][j] = Max{F[i-1][j],F[i-1][j-Vi]+W[i]}.

若对空间有所闲置,则由于F[i][j]只与F[i-1]有关,可以使用滚动数组的形式进行解答。

伪代码如下:

for i:=0 to n
    0 ->    F[i][0]
for j:=0 to C
    0 -> F[0][j]

for i:=1 to n
    for j:=0 to C
        if j < Vi
            F[i][j] = F[i-1][j]
        else
            F[i][j] = Max{F[i-1][j], F[i-1][j-Vi]+W[i] }
    END FOR
END FOR

最终结果为F[n][C]


完全背包

完全背包问题:给定n种物品和一个背包。第i种物品的价值是Wi,其体积为Vi,背包的容量为C,同一种物品的数量无限。可以任意选择装入背包的物品,求装入的最大价值。

思考一下与0-1背包问题的不同:仅为物品数量的闲置。现在可以选择同种物品的数量,那么根据之前的思路,F[i][j]应该为什么?
若j < Vi,则F[i][j] = F[i-1][j];
否则:F[i][j] = Max{F[i-1][j], F[i][j-Vi]+Wi}

值得注意的是,式中的黑体部分:F[i][j-Vi], 而不是如0-1问题中的 F[i-1][j-Vi],这是因为,0-1问题中,每个物品只能拿一次,当判断完第i-1个物品之后(不管拿还是没拿),这第i-1个物品就算过去了,因此F[i]跟且只跟F[i-1]有关。而本题中,由于第i个物品有无数个,因此可以无限的装(只要装得下),所以F[i][j]不跟F[i-1]有关了(当j>Vi的时候),而是同一个物品又装一个之前的状态有关,即跟F[i][j-Vi]有关,也因此导致了状态转移方程中的区别。

伪代码如下:

for i:=0 to n
    0 ->    F[i][0]
for j:=0 to C
    0 -> F[0][j]

for i:=1 to n
    for j:=0 to C
        if j < Vi
            F[i][j] = F[i-1][j]
        else
            F[i][j] = Max{F[i-1][j], F[i][j-Vi]+W[i] }
    END FOR
END FOR

最终结果为F[n][C]


多次背包*

多次背包问题:给定n种物品和一个背包。第i种物品的体积为Vi,价值为Wi,数量为Ki,背包的容量为C。可以任选装入背包,求装入背包的最大价值。

首先对于第i种物品,不能确定放入多少个是最优的,即最优解所需要的件数可能是0~Ki中的任何一个。
那么能否像完全背包去考虑呢?
完全背包中,当j>Vi时,转移方程为F[i][j] = Max{F[i-1][j], F[i][j-Vi]+Wi}
但是粗体部分是建立在第i个物品的数量是无限多的情况下,想拿多少就有多少的情况。
在本题中Ki是数量的上限,所以肯定不能使用上述方程。

但是类似的,我们可以对Ki中每增加一件物品i时进行一次计算,转移方程如下:
若j < Vi,F[i][j] = F[i-1][j]
否则,F[i][j] = Max{F[i-1][j], F[i][j-k*Vi]}(1<=k<=Ki & k*Vi<=j<=C)
但是,每次对k增加1似乎过于浪费,即在k到达Ki的过程中,不需要每次加1。可以证明,按照二进制的拆分能使件数达到最小,把Ki拆分成1,2,4,…,2^t,Ki-2^(t+1)+1 (2^(t+2)>Ki>=2^(t+1))

伪代码如下:

FOR i:=1 TO n
    1->m
    WHILE Ki>0
        IF m>Ki THEN Ki->m
        Ki-m->Ki
        FOR j:= C DOWNTO Vi*m
            MAX(F[i-1][j], F[i][j-Vi*m]+Wi*m)->F[i][j]
        END FOR
    m*2->m
    WEND
END FOR

趣题——三类背包问题

标签:dp   背包   趣题   

原文地址:http://blog.csdn.net/langduhualangdu/article/details/43852871

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!