多重背包暴力DP为$O(nV^2)$,n为物品个数,V为背包容量,二进制优化复杂度为$O(nVlog V)$。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=1010; 7 int T,n,m,v,w,tot,f[N]; 8 9 int main(){ 10 for (scanf("%d",&T); T--; ){ 11 scanf("%d%d",&m,&n); 12 rep(i,0,m) f[i]=0; 13 rep(i,1,n){ 14 scanf("%d%d%d",&v,&w,&tot); 15 for (int j=1; tot>=j; tot-=j,j<<=1) 16 for (int k=m; k>=j*v; k--) f[k]=max(f[k],f[k-j*v]+j*w); 17 if (!tot) continue; 18 for (int k=m; k>=tot*v; k--) f[k]=max(f[k],f[k-tot*v]+tot*w); 19 } 20 printf("%d ",f[m]); 21 } 22 return 0; 23 }
单调队列优化复杂度为$O(nV)$。
单调队列的主要思想是从DP方程入手:$$f_{i+j imes v}=max{f_{i+k imes v}+(j-k) imes w}=max{f_{i+k imes v}-k*w}+j*w$$
v为当前物品的代价,w为价值。j-tot<=k<=j,tot为当前物品的个数。
发现所有转移都是在同一剩余系下做的,所以只要对每个剩余系分别维护一个单调队列即可。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=1010; 7 int T,n,m,v,w,tot,q1[N],q2[N],f[N]; 8 9 void DP(int v,int w,int tot){ 10 for (int i=0; i<v; i++){ 11 int st=0,ed=0; q1[0]=q2[0]=0;//单调队列,q1[]存队列内下标,q2[]存队列内的值 12 for (int j=0; j<=(m-i)/v; j++){//f[i+j*v[i]]=max{f[i+k*v[i]]+k*w[i]}+j*w[i](j-tot<=k<=j) 13 if (st<=ed && q1[st]<j-tot) st++;//j-tot<=k<=j 14 int t=f[i+j*v]-j*w;//队列中存f[i+j*v]-j*w 15 f[i+j*v]=max(f[i+j*v],q2[st]+j*w);//更新f[i+j*v] 16 while (st<=ed && t>=q2[ed]) ed--;//将j放入队列 17 q1[++ed]=j; q2[ed]=t; 18 } 19 } 20 } 21 22 int main(){ 23 freopen("hdu2191.in","r",stdin); 24 freopen("hdu2191.out","w",stdout); 25 for (scanf("%d",&T); T--; ){ 26 scanf("%d%d",&m,&n); int mx=0; 27 rep(i,0,m) f[i]=0; 28 rep(i,1,n) scanf("%d%d%d",&v,&w,&tot),DP(v,w,tot); 29 rep(i,1,m) mx=max(mx,f[i]); printf("%d ",mx); 30 } 31 return 0; 32 }