#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值
*/