混合三种背包问题
问题:
如果将 01背包问题、完全背包问题、多重背包问题混合起来。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?
一、背包与完全背包的混合
考虑到在 01背包问题 和 完全背包问题 中给出的伪代码只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是O(VN)。伪代码如下:
for i=1..N
if 第i件物品是01背包
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
else if 第i件物品是完全背包
for v=0..V
f[v]=max{f[v],f[v-c[i]]+w[i]};
例题:
旅行者有一个容量为V公斤的背包
n :物品种数
w :物品重量
v :物品价值
p :0表示可取无穷多件,1表示可取一件
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
输入样例:
10 3
2 1 0
3 3 1
4 5 0
输出样例:
11
#include<cstdio>
#include<iostream>
using namespace std;
int V,n;
int w[31],v[31],p[31];
int f[201];
int max(int x,int y)
{
if(x<y)return y;
else return x;
}
int main(){
scanf("%d%d",&V,&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&w[i],&v[i],&p[i]);
for(int i=1;i<=n;i++){
if(p[i]==0) //完全背包
{
for(int j=w[i];j<=V;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
else //01背包
{
for(int j=V;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
printf("%d",f[V]);
return 0;
}
二、再加上多重背包
如果再加上有的物品最多可以取有限次,那么可以用 多重背包问题中 将每个这类物品分成 个 01背包的物品的方法。
当然,更清晰的写法是调用我们前面给出的三个相关过程。
for i=1..N
if 第i件物品是01背包
ZeroOnePack(c[i],w[i]) //调用01背包问题函数
else if 第i件物品是完全背包
CompletePack(c[i],w[i]) //调用完全背包问题函数
else if 第i件物品是多重背包
MultiplePack(c[i],w[i],n[i]) //调用多重背包问题函数
例题:
旅行者有一个容量为V公斤的背包
n :物品种数
w :物品重量
v :物品价值
p :物品可以拿取的数目(0表示可取无穷多件)
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
输入样例:
10 3
2 1 0
3 3 1
4 5 4
输出样例:
11
#include<cstdio>
#include<iostream>
using namespace std;
int V,n;
int w[31],v[31],p[31];
int f[201];
int max(int x,int y)
{
if(x<y)return y;
else return x;
}
int main(){
scanf("%d%d",&V,&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&w[i],&v[i],&p[i]);
for(int i=1;i<=n;i++){
if(p[i]==0) //完全背包
{
for(int j=w[i];j<=V;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
else
{
for(int j=1;j<=p[i];j++) //01背包和多重背包
for(int k=V;k>=w[i];k--)
f[k]=max(f[k],f[k-w[i]]+v[i]);
}
}
printf("%d",f[V]);
return 0;
}