经典例题:多重背包
洛谷 P1776 宝物筛选 https://www.luogu.com.cn/problem/P1776
输入:
第一行是整数 n 和 W,分别表示物品种数和背包的最大容量。
接下来 n 行,每行三个整数 vi、wi、mi,分别表示第i个物品的价值、体积、数量。
输出:
输出一个整数,表示背包不超载的情况下装入物品的最大价值。
题解:例如第i种物品有mi=25个,这25个物品放进背包的组合,有0~25的26种情况。不过要组合成26种情况,其实并不需要25个物品。根据二进制的计算原理,任何一个十进制整数X,都可以用1、2、4、8…这些2的倍数相加得到,故可以将25个物品分为1,2,4,8,10倍于原物品的新物品。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXX=100010;
int n,W,dp[MAXX];
int v[MAXX],w[MAXX],m[MAXX];
int new_n; //二进制拆分后的新物品总数量
int new_v[MAXX],new_w[MAXX],new_m[MAXX]; //二进制拆分后新物品
int main(){
cin >> n >>W;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>m[i];
//以下是二进制拆分
int new_n = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m[i];j<<=1) { //二进制枚举:1,2,4...
m[i]-=j; //减去已拆分的
new_w[++new_n] = j*w[i]; //新物品
new_v[new_n] = j*v[i];
}
if(m[i]){ //最后一个是余数
new_w[++new_n] = m[i]*w[i];
new_v[new_n] = m[i]*v[i];
}
}
//以下是滚动数组版本的0/1背包
for(int i=1;i<=new_n;i++) //枚举物品
for(int j=W;j>=new_w[i];j--) //枚举背包容量
dp[j]=max(dp[j],dp[j-new_w[i]]+new_v[i]);
cout << dp[W] << endl;
return 0;
}