多重背包
每种物品有若干个,物品i的体积:w[i]、价值:v[i]、数量:amount
第i轮:dp[j]:前i个物品装进容量为j的背包所获得的最大价值
采用二进制 将同种物品组合成新的物品,化为0-1背包求解
#include<iostream>
#include<string>
using namespace std;
const int maxn=500010;
int w[maxn];//体积
int v[maxn];//价值
int dp[maxn];
int best(int sum,int n){//0-1背包(优化)
for(int i=0;i<=sum;i++)dp[i]=0;
for(int i=1;i<=n;i++){
for(int j=sum;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
return dp[sum];
}
int main(){
int sum,n;
while(scanf("%d%d",&sum,&n)!=EOF){
int index=1;//物品起始下标
for(int i=0;i<n;i++){
int amount,volume,value;
scanf("%d%d%d",&amount,&volume,&value);
int j=1;
while(amount>=j){//相同物品重新组合
amount=amount-j;
w[index]=volume*j;
v[index]=value*j;
index++;
j=j*2;//二进制
}
if(amount>0){
w[index]=volume*amount;
v[index]=value*amount;
index++;
}
}
printf("%d\n",best(sum,index-1));
}
}
看到官方给的题解很妙,相当于输入一个就动态递推一次
- 如果当前物品总体积大于背包容量:本次递推为完全背包
- 否则将其按照1,2,4,8...二进制重新组合成新的物品,本次递推为0-1背包
#include<iostream>
using namespace std;
const int maxn=10001;
int w[maxn];//体积
int v[maxn];//价值
int dp[maxn];
int main(){
int sum,n;
while(scanf("%d%d",&sum,&n)!=EOF){
for(int i=0;i<=sum;i++)dp[i]=0;//初始化
for(int i=0;i<n;i++){
int amount,volume,value;
scanf("%d%d%d",&amount,&volume,&value);
if(amount*volume>sum){//完全背包
for(int j=volume;j<=sum;j++){//从前往后
dp[j]=max(dp[j],dp[j-volume]+value);
}
}
else{//0-1背包
int j=1;
int index=1;//拆分后物品起始下标
while(amount>=j){//物品拆分
amount=amount-j;
w[index]=volume*j;
v[index]=value*j;
index++;
j=j*2;//二进制拆分
}
if(amount>0){
w[index]=volume*amount;
v[index]=value*amount;
index++;
}
for(int i=1;i<index;i++){
for(int j=sum;j>=w[i];j--){//从后往前
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
}
printf("%d\n",dp[sum]);
}
}