1.题目
有 N𝑁 种物品和一个容量是 V𝑉 的背包。
物品一共有三类:
- 第一类物品只能用1次(01背包);
- 第二类物品可以用无限次(完全背包);
- 第三类物品最多只能用 si𝑠𝑖 次(多重背包);
每种体积是 vi𝑣𝑖,价值是 wi𝑤𝑖。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V𝑁,𝑉,用空格隔开,分别表示物品种数和背包容积。
接下来有 N𝑁 行,每行三个整数 vi,wi,si𝑣𝑖,𝑤𝑖,𝑠𝑖,用空格隔开,分别表示第 i𝑖 种物品的体积、价值和数量。
- si=−1𝑠𝑖=−1 表示第 i𝑖 种物品只能用1次;
- si=0𝑠𝑖=0 表示第 i𝑖 种物品可以用无限次;
- si>0𝑠𝑖>0 表示第 i𝑖 种物品可以使用 si𝑠𝑖 次;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤10000<𝑁,𝑉≤1000
0<vi,wi≤10000<𝑣𝑖,𝑤𝑖≤1000
−1≤si≤1000−1≤𝑠𝑖≤1000
输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8
2.思路
本题需要用到完全背包和01背包问题的知识,如若有不了解或有遗忘,可跳转到下面页面进行学习:
对于混合背包,我们可以清晰的看出这是三个背包问题的整合,而为了解决这个问题,我们我们可以将其中的01背包问题和完全背包问题均转化为多重背包问题,然后使用多重背包问题的二进制优化来解决该题。
01背包转换为多重背包,毋庸置疑只需要在tempv,tempw(存放优化后的体积和价值的列表)两个列表中分别加上对应的物品,因为01背包中的物品只有一件,也就是只能使用一次,所以直接加入就行
完全背包转换为多重背包,因为完全背包中的物品可以使用无数次,也就相当于有无数件物品,但我们知道,背包它是有总体积的,虽然物品可以一直使用,但它由于背包总体积的限制,导致它的范围上限变成了:v/物品体积,这样完全背包就转换成了多重背包问题。然后使用二进制优化的写法把其添加到优化后的列表中
全部转换完成后,此时也相当于完成了二进制优化,之后便可以使用一维dp滚动列表求出最大价值
3.代码
python代码:
n,v=map(int,input().split())#输入n,v
bag=[]
for i in range(n):
bag.append(list(map(int,input().split())))#将体积,价值,类型全部放入bag中
tempv=[]#用于存放二进制优化后的体积
tempw=[]#用于存放二进制优化后的价值
newn=0#用于统计优化后总共的物品
for i in range(n):
if(bag[i][2]==-1):#01背包
tempv.append(bag[i][0])
tempw.append(bag[i][1])
newn=newn+1
elif(bag[i][2]==0):#完全背包
k = 1
x=v//bag[i][0]#因为总体积的缘故,所以虽然其为完全背包的类型,终还是有上限
while(k<=x):
tempv.append(k*bag[i][0])
tempw.append(k*bag[i][1])
x=x-k
k=k*2
newn=newn+1
if(x!=0):
tempv.append(x*bag[i][0])
tempw.append(x*bag[i][1])
newn = newn + 1
elif(bag[i][2]>0):#多重背包
k = 1
while (k <= bag[i][2]):
tempv.append(k*bag[i][0])
tempw.append(k*bag[i][1])
bag[i][2] = bag[i][2] - k
k = k * 2
newn = newn + 1
if (bag[i][2] != 0):
tempv.append(bag[i][2] * bag[i][0])
tempw.append(bag[i][2] * bag[i][1])
newn = newn + 1
dp=[0 for i in range(v+1)]
def fun(newn,tempv,tempw,v):#一维滚动dp列表求最大价值
for i in range(1, newn + 1):
for j in range(v, -1, -1):
if (j >= tempv[i - 1]):
dp[j] = max(dp[j], dp[j - tempv[i - 1]] + tempw[i - 1])
else:
dp[j] = dp[j]
print(dp[-1])
fun(newn,tempv,tempw,v)