多重背包单调队列优化:
dp[c+i*p]=max(dp[c+k*p]+(i-k)*h);
每种背包的容量p,价值h,c的范围为0~p-1,i和k的范围为0~num(能够取的背包的数量)
背包的取法可以看做是划分为0~p-1这p个容量再加上i个背包。
dp[c+i*p]=dp[c+k*p]-k*h+i*h;
代码:
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<map>
#include<cstdio>
#include<cstring>
#define maxn 2000
#define INF 0xfffffff
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;
struct node
{
int f;
int pos;
};
node q[maxn];
int dp[maxn];
int n,m,p,h,num,head,tail,nowf;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);//总钱数n
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&p,&h,&num);//价格,重量,袋数
num=min(num,n/p);
for(int j=0;j<p;j++)//0~p-1种
{
head=tail=0;
for(int k=0;k<=(n-j)/p;k++)
{
nowf=dp[j+p*k]-k*h;
while(head<tail&&q[tail-1].f<=nowf)
tail--;
q[tail].f=nowf,q[tail++].pos=k;
while(head<tail&&q[head].pos<k-num)//是否能通过增加num达到k
head++;
dp[j+k*p]=q[head].f+k*h;
}
}
}
printf("%d\n",dp[n]);
}
return 0;
}