#include <stdio.h> #include <string.h> #define INF 1000000000 int max(int a,int b) { return a>b?a:b; } int f[111]; int c[111],w[111],d[111]; int main() { int T; scanf("%d",&T); while(T--) { int n,m,i,j,k; scanf("%d%d",&m,&n); for(i=0;i<n;i++) scanf("%d%d%d",&c[i],&w[i],&d[i]); memset(f,0,sizeof(f)); for(i=0;i<n;i++) { if(c[i]*d[i]<=m) { for(k=1;k<d[i];k=k*2)//多重转换成01背包 { //举例1至24的数可以转换成1,2,4,8,9的任意组合 for(j=m;j>=c[i]*k;j--)//那么可以讲输入的p,h,24,转成p,h*1;p,h*2;p,h*4;p,h*8;p,h*9的01背包了 f[j]=max(f[j],f[j-c[i]*k]+w[i]*k); d[i]-=k; } for(j=m;j>=c[i]*d[i];j--) f[j]=max(f[j],f[j-c[i]*d[i]]+w[i]*d[i]); } else //总数超过m,可以看做完全背包 for(j=c[i];j<=m;j++) f[j]=max(f[j-c[i]]+w[i],f[j]); } printf("%d\n",f[m]); } return 0; }
下面是套用单调队列的多重背包的代码,时间复杂度只有O(vn);个人理解可能有误,请自行分析
#include<stdio.h> #include<string.h> #define MAXN 111 struct Queue { int num,value; }que[MAXN]; int head,tail; int dp[MAXN]; void enqueue (int x , int y) { while (que[tail].value<y && head<=tail) tail--;//删除队列中比插入数小的值,确保队列从大到小 que[++tail].num=x; que[tail].value=y; } int c[111],w[111],v[111]; int main() { int i,j,d,n,m; int T; scanf("%d",&T); while (T--) { scanf("%d%d",&m,&n); for (i =0; i < n ; ++i) scanf("%d%d%d",&v[i],&w[i],&c[i]); memset(dp,0,sizeof(dp)); for (i=0 ; i<n ; ++i) { if (c[i] == 0) continue; if (c[i] > m/v[i]) c[i]=m/v[i];//c[i]*v[i]>m时就是完全背包,它的处理方式跟c[i]*v[i]=m时一样 //总体积是10,当前体积是4,则dp顺序是{{0,4,8},{1,5,9},{2,6,10},{3,7}} for (d=0 ; d<v[i] ; ++d) { head=1;tail=0; for (j=0 ; j<=(m-d)/v[i] ; ++j)//j-1的dp下标正好是j的dp下标-v[i] { enqueue(j , dp[j*v[i]+d]-j*w[i]); while (que[head].num < j-c[i]) head++;//去掉最大值(如果最大值不是后面c+1个数中的数) dp[j*v[i]+d]=que[head].value+j*w[i]; } } } printf("%d\n",dp[m]); } return 0; } /* dp公式是dp[i][j]=max(dp[i-1][j-k*v[i]]+k*w[i])(0<=k<=c[i]) v为当前体积,w是当前价值,c是当前个数 由上式可以知道 c=2时,l<v dp[4*v+l]=max(dp[4*v+l],dp[3*v+l]+w,dp[2*v+l]+2*w) dp[5*v+l]=max(dp[5*v+l],dp[4*v+l]+w,dp[3*v+l]+2*w) dp[6*v+l]=max(dp[6*v+l],dp[5*v+l]+w,dp[4*v+l]+2*w) 入队列时:(单调队列,保持第一个数是最大值) dp[l],dp[l+v]-w,dp[l+2*v]-2*w,dp[l+3*v]-3w,dp[l+4v]-4w 用while (que[head].num < j-c[i]) head++;去掉最大值(如果最大值不是后面c+1个数中的数) 出队列,出最大值 dp[l+4v]=max(dp[l+2*v]-2*w,dp[l+3*v]-3w,dp[l+4*v]-4w)+4w=max(dp[4*v+l],dp[3*v+l]+w,dp[2*v+l]+2*w) dp[l+5v]-5w入队列 出队列dp[l+5v]=max(dp[l+3*v]-3w,dp[l+4*v]-4w,dp[l+5*v]-5w)+5w=max(dp[5*v+l],dp[4*v+l]+w,dp[3*v+l]+2*w) 正好构成循环 依此类推,枚举l(0<=l<v)求出所有dp值 */