混合背包问题求解

题目:

在这里插入图片描述

解答:

方法一:

N,V=map(int,input().split())  #输入物品种数和背包容量
m=[0]*(V+1)  #新建一个长度为V+1的list m

for i in range(N):
    v,w,s=map(int,input().split())  #输入第i种物品的体积、价值和数量
    if s==-1:  #第i种物品只能用1次,01背包
        for j in range(V,-1,-1):
            if v<=j:
                m[j]=max(m[j],m[j-v]+w)  #每次只能取一个
    elif s==0:  #第i种物品可以用无限次,完全背包
        for j in range(v,V+1): 
            m[j]=max(m[j],m[j-v]+w)  #每次可以取多个
    else:  #第i种物品最多只能用si次,多重背包
        for j in range(V,-1,-1):
            for k in range(1,s+1):
                if v*k<=j:
                    m[j]=max(m[j],m[j-v*k]+w*k)  #每次可以取多个
        
print(m[V])  #输出最大价值

方法一将通过条件语句将三种背包问题分开处理,同时对所有的背包问题使用同一个动态转移函数使它们的转台转移状态一致,解决该问题。但是该方法存在缺陷,主要是对于多背包问题的处理,时间复杂度要求较高,无法通过所有的测试案例。
在这里插入图片描述

方法二:

参考他人的思路写的求解方法,主要思路是将问题中的所有背包问题都转换成多背包问题。01背包问题,转换成出现次数为1的多背包问题;完全背包问题,用背包的总容量除以完全背包的体积,得到的结果设置为该背包出现的次数。最后使用二进制优化方法处理这个转化后的多背包问题。
原帖链接

n,m=map(int,input().split()) #输入n和m

v,w=[],[] #创建两个数组

f=[0 for i in range(100010)] #创建一个长度100010的数组并全部初始化为0 

cnt=0

for i in range(1,n+1): #循环n遍
    a,b,s=map(int,input().split()) #输入a,b,s
    k=1
    if s<0: #如果s小于0
        s=1
    elif s==0: #如果s等于0
        s=m//a #s等于总体积除以a 
    while k<=s: #当k小于等于s时
        v.append(a*k) #将a*k添加至v数组
        w.append(b*k) #将b*k添加至w数组
        s-=k #s减去k
        k*=2 #k乘以2 
        cnt+=1 #cnt加1
    if s>0: #如果s大于0
        v.append(s*a) #将s*a添加至v数组
        w.append(s*b) #将s*b添加至w数组
        cnt+=1 #cnt加1

#将多重背包进行二进制优化,变成01背包
for i in range(1,cnt+1): #循环到cnt
    for j in range(m,v[i-1]-1,-1): #倒序遍历m到v[i]
        f[j]=max(f[j],f[j-v[i-1]]+w[i-1]) #f[j]等于f[j]和f[j-v[i]]+w[i]之间较大的值

#01背包问题
print(f[m]) #输出f[m]

在这里插入图片描述
方法二通过测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_56318237

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值