多重背包:n个物品,背包承重m,每个物品 重量:wi 价值vi 个数为ci
普通多重背包复杂度:O(nmc)
代码:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=c[i] && k*w[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
单调队列优化的多重背包复杂度:O(nm)
考虑把O(c )的转移压缩为O(1)
变形:
a=j/w[i]
b=j%w[i]
j=b+aw[i]
思路:承重为j时,不考虑ci限制时最多选a个i号元素。假设a个中不选k个,即选a-k个。
转移方程:
f[i][j]=max(f[i][j],f[i-1][j-(a-k)*w[i]]+(a-k)*v[i]);
等价于
f[i][j]=max(f[i][j],f[i-1][b+k*w[i]]+(a-k)*v[i]);
即是
f[i][j]=max(f[i][j],f[i-1][b+k*w[i]]-k*v[i])+a*v[i];//(a-k<=c[i])
这样max里面就与j无关了,只与b和k有关。
那么我们枚举b和k就行了。然后用单调队列维护max。
b+a*w[i]即为j恰好可以表示1~m的所有数
求f[i][j]用到哪些状态,我来列举下就一目了然了
f[i][b+0*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i])+0*v[i];
f[i][b+1*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i])+1*v[i];
f[i][b+2*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]])+2*v[i];
....
f[i][b+k*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]]....f[i-1][b+a*w[i]]-k*v[i])+k*v[i];
....
f[i][b+a*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]]....f[i-1][b+a*w[i]]-a*v[i])+a*v[i];
可以发现f[i][b+k*w[i]]的转移规律,我们就拿单调队列把max里的东西存下来,这样就不用每次枚举选多少个i号物品了
代码片:
for(int b=0; b<w[i]; b++) {
he=ta=1;
for(int k=0; k<=(m-b)/w[i]; k++) {//a=(m-b)/w[i],0<=k<=a
int j=k*w[i]+b;
int tmp=f[i-1][b+k*w[i]]-k*v[i];
while(he<ta && tmp>q[ta-1])//保留当前最优的
ta--;
q[ta]=tmp;
num[ta++]=k;//不选k个
while(he<ta && k-num[he]>c[i]) //选的个数为 k-num[he],且不大于c[i],如果大于了c[i]就删掉
he++;
f[i][j]=q[he]+k*v[i];
}
}
例题:hdu2191
#include <bits/stdc++.h>
using namespace std;
#define N 110
#define inf 999999999
int t,n,m,w[N],v[N],c[N],ans,he,ta;
int f[N][7005],q[7005],num[7005];
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&m,&n);
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
scanf("%d%d%d",&w[i],&v[i],&c[i]);
for(int b=0;b<w[i];b++){
he=ta=1;
for(int k=0;k<=(m-b)/w[i];k++){
int j=k*w[i]+b;
int tmp=f[i-1][b+k*w[i]]-k*v[i];
while(he<ta && tmp>q[ta-1]) ta--;
q[ta]=tmp;num[ta++]=k;
while(he<ta && k-num[he]>c[i]) he++;
f[i][j]=q[he]+k*v[i];
}
}
}
printf("%d\n",f[n][m]);
}
return 0;
}