完全背包与多重背包

完全背包问题

提出问题:
现在有 N N N个物品,和一个容量为 V V V的背包,每一种物品都有无限件可以使用。第 i i i种物品的费用是 C i C_{i} Ci,价值是 W i W_{i} Wi.求解将那些物品装入背包,在总重量不超过背包容量的情况下,使得背包内的物品价值最大。

求解问题:
对于每一个物品,我们有无限件,对于每一个物品,问题已经不是取或者是不取,而是选取0件、取1件、取2件直到取 ⌊ V / C i ⌋ \left\lfloor V/C_{i} \right\rfloor V/Ci
按照01背包的思路:建立动态规划表F[i,v]表示前i种物品放在一个容量为v的背包的最大价值。现在写出状态方程:
F [ i , v ] = m a x { F [ i − 1 , j − k C i ] + k W i   ∣   0 ≤ k C i ≤ v } F[i,v] = max\{F[i-1,j-kC_{i}] + kW_{i}\, | \, 0 \leq kC_{i} \leq v\} F[i,v]=max{F[i1,jkCi]+kWi0kCiv}

def completePack(w,v,maxCap):
    dp = [[0]*(maxCap+1) for _ in range(len(w)+1)]
    for i in range(1,len(w)+1):
        for j in range(1,maxCap+1):
            if j < w[i-1]:
                dp[i][j] = dp[i-1][j]
            else:
                maxnum = 0
                for k in range((j//w[i-1])+1):
                    maxnum = max(maxnum,dp[i-1][j-k*w[i-1]] + k*v[i-1])
                dp[i][j] = maxnum
    return dp

w = [2,3,4,5]
v = [3,4,5,6]
V = completePack(w,v,10)

V
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 3, 3, 6, 6, 9, 9, 12, 12, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15]]

求解状态方法,需要有 O ( V N ) O(VN) O(VN)个状态求解,但是求解每一个状态的时间已经不是常数时间,求解F[i,v]的时间是 O ( V C i ) O(\frac{V}{C_{i}}) O(CiV),所以总的复杂度是 O ( N V ∑ V C i ) O(NV\sum\frac{V}{C_{i}}) O(NVCiV).

一个简单有效的优化:

  • 现在有两件物品i,j满足 C i ≤ C j C_{i} \leq C_{j} CiCj W i ≥ C j W_{i} \geq C_{j} WiCj,就可以直接去掉物品j。
  • 如果物品的重量大于背包容量可以直接去掉。
  • 如果重量相同,我们取出价值最高的物品。

转换为01背包问题求解:把第i种物品换成 M i M_{i} Mi件01背包的物品, M i = ⌊ V C i ⌋ M_{i} = \left\lfloor \frac{V}{C_{i}} \right\rfloor Mi=CiV.所以我们现在就有 ∑ M i \sum M_{i} Mi的物品。
现在建立动态规划的状态表。
F [ i ] [ j ] : F[i][j]: F[i][j]:表示容量为j的背包装到前i个物品物品的最大价值。
那么问题F[i][j]:就会变成不装这个物品 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j],多装一个这个物品 d p [ i ] [ j − w i ] dp[i][j-w_{i}] dp[i][jwi]
我们写出递归式:
F [ i , v ] = m a x ( F [ i − 1 , j ] , F [ i , j − w i ] + v i ) F[i,v] = max(F[i-1,j], F[i,j-w_{i}] + v_{i}) F[i,v]=max(F[i1,j],F[i,jwi]+vi)

def completePack(w,v,maxCap):
    dp = [[0]*(maxCap+1) for _ in range(len(w)+1)]
    for i in range(1,len(w)+1):
        for j in range(1,maxCap+1):
            if j < w[i-1]:
                dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-w[i-1]]+ v[i-1])
    return dp
w = [2,3,4,5]
v = [3,4,5,6]

V = completePack(w,v,10)
V
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 3, 3, 6, 6, 9, 9, 12, 12, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15],
 [0, 0, 3, 4, 6, 7, 9, 10, 12, 13, 15]]
def completePack(w,v,maxCap):
    dp = [0]*(maxCap+1)
    for i in range(1,len(v)+1):
        for j in range(1,maxCap+1):
            if j >= w[i-1]:
                dp[j] = max(dp[j], dp[j-w[i-1]] + v[i-1])
    return dp
def allComplete(dp,w,v,maxCap):
    for i in range(1,len(v)+1):
        completePack(dp,w[i-1],v[i-1],maxCap)
def completePack(dp,w,v,maxCap):
    for j in range(1,maxCap+1):
        if j >= w:
            dp[j] = max(dp[j], dp[j- w] + v)
w = [2,3,4,5]
v = [3,4,5,6]
maxCap = 8
dp = [0]*(maxCap+1)
allComplete(dp, w,v,maxCap)
dp
[0, 0, 3, 4, 6, 7, 9, 10, 12]

3.多重背包问题

提出问题:
有N种物品和一个容量为V的背包。第i种物品最多有 M i M_{i} Mi件可用,每件耗费的空间是 C i C_{i} Ci,价值是 W i W_{i} Wi。求解怎么放物品使得背包装的物品价值最大.

基本算法:
对于第i种物品有 M i + 1 M_{i}+1 Mi+1种策略:取0件、取1件、…、取 M i + 1 M_{i}+1 Mi+1件。 F [ i , v ] F[i,v] F[i,v]表示前i种物品放入一个容量为v的背包的最大价值。
F [ i , j ] = m a x ( F [ i − 1 , j − k ∗ C i ] + k ∗ W i &ThinSpace; ∣ &ThinSpace; 0 ≤ k ≤ M i ) F[i,j] = max(F[i-1,j - k * C_{i}] + k * W_{i}\, |\, 0 \leq k \leq M_{i} ) F[i,j]=max(F[i1,jkCi]+kWi0kMi)

def mutiplePack(dp, w , v , m, maxCap):
    for i in range(1,len(w)+1):
        for j in range(1,maxCap+1):
            if j < w[i-1]:
                dp[i][j] = dp[i-1][j]
            else:
                maxnum = 0
                for k in range(m[i-1]+1):
                    if k * w[i-1] <= j:
                        maxnum = max(maxnum, dp[i-1][j-k*w[i-1]] + k*v[i-1])
                dp[i][j] = maxnum
w = [2,3,4,5]
v = [3,4,5,6]
m = [3,3,2,2]
maxCap = 8
dp  = [[0]*(maxCap+1) for _ in range(len(w)+1)]
mutiplePack(dp,w,v,m,maxCap)
dp
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 3, 3, 6, 6, 9, 9, 9],
 [0, 0, 3, 4, 6, 7, 9, 10, 11],
 [0, 0, 3, 4, 6, 7, 9, 10, 11],
 [0, 0, 3, 4, 6, 7, 9, 10, 11]]
转换为01背包问题
def zeroOnePack(dp,w,v,maxCap):
    for j in range(maxCap,0,-1):
        if j >= w:
            dp[j] = max(dp[j],dp[j-w]+v)
def allZeroOne(dp,w,v,maxCap):
    for i in range(len(w)+1):
        zeroOnePack(dp,w[i-1],v[i-1],maxCap)
w = [2,3,4,5]
v = [3,4,5,6]
maxCap = 8
dp = [0]*(maxCap+1)
allZeroOne(dp,w,v,maxCap)
dp
[0, 0, 3, 4, 5, 7, 8, 9, 10]
def allMutiplePack(dp,w,v,m,maxCap):
    for i in range(len(w)+1):
        multiplePack(dp,w[i-1],v[i-1],m[i-1], maxCap)
def multiplePack(dp, w, v, m, maxCap):
    if w * m >= maxCap:
        completePack(dp,w,v, maxCap)
    k = 1
    while k < m:
        zeroOnePack(dp,k*w,k*v, maxCap)
        m = m - k
        k = 2*k
    zeroOnePack(dp, w * m, v * m ,maxCap)
        
w = [2,3,4,5]
v = [3,4,5,6]
m = [2,2,3,3]
maxCap = 8
dp  = [0]*(maxCap+1)
allMutiplePack(dp,w,v,m,maxCap)
dp
[0, 0, 3, 4, 6, 7, 8, 10, 11]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值