多重背包可以拆分为01背包来做,最暴力的想法是把每类物品拆成一个个的,然后拆分后的物品各自为一类,做01背包。然而这样太慢了,我们可以用二进制(倍增)来优化拆分。
比如说我们有
1
10
100
1000
10000
这些二进制数,他们代表1, 2, 4, 8, 16
那么这些数可以组合出1~31之间所有的数
所以把物品倍增拆分,一样可以得到正确的答案。
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1010;
struct Bread{
int mx, v, w;//最大个数,价值,花费/重量
}bre[MAXN];
int n,m,c0,d0,ans,sum;
int f[MAXN];
int main() {
cin >> n >> m >> c0 >> d0;
bre[0].mx = n/c0,bre[0].v = d0,bre[0].w = c0;
for(int i=1; i<=m; i++) {
int a,b,c,d;
cin >> a >> b >> c >> d;
bre[i].mx = a/b;
bre[i].v = d;
bre[i].w = c;
}
for(int i=0; i<=m; i++) {
int a[22], t = 0, sum = 0;//循环到20 开到22
for(int k=0; k<=20; k++) {
sum += 1<<k;
if(sum > bre[i].mx) {
sum -= 1<<k;
break;
}
a[++t] = (1<<k);
}
a[++t] = bre[i].mx - sum; //二进制划分后剩下的部分
for(int k=1; k<=t; k++) {
int vk = bre[i].v*a[k];
int wk = bre[i].w*a[k];
for(int j=n; j>=wk; j--) {
f[j] = max(f[j],f[j-wk]+vk);
}
}
}
cout << f[n];
return 0;
}