题目
见HDU2191(其实就是一道多重背包)
Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400
思路
对于每一个物品,我们枚举其剩余类,具体来说,就是把一个容量为j的背包在只考虑该物品情况下,能够按从哪些部分转移过来,也就是候选集合,举例来说,如果j=5,该物品(质量W,价值V,件数C)W=2,那么余数为1的类有2,4
所以,我们一重枚举物品,一重枚举余数,然后一重枚举件数,没错,每一次枚举余数都需要单调队列,但是它的时间复杂度依然为O(nm)
code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<deque>
#include<cstdio>
#include<map>
using namespace std;
deque<int> a;
int a2[1001];
int c2[1001],mx,t,v,w,c,b,k;
int n,p,q,i,m;
void read(int& x)
{
x=0;
int f=1;
char ch=getchar();
while (!isdigit(ch)) (ch=='-')&&(f=-1),ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x*=f;
}
void wr(int x)
{
(x<0)&&(x=-x,putchar('-'));
if (x>9) wr(x/10);
putchar(x%10^48);
}
int main()
{
read(t);
while (t--)
{
read(m),read(n);
memset(a2,0,sizeof(a2));
for (i=1;i<=n;i++)
{
read(w),read(v),read(c);//v价值,w体积,c数量
for (b=0;b<w;b++)
{
int mxp=(m-p)/w;//最大件数
for (k=mxp-1;k>=max(mxp-c,0);k--)//买mxp件的候选集合
{
while (a.size()&&a2[a.back()*w+b]-a.back()*v<=a2[k*w+b]-k*v) a.pop_back();
a.push_back(k);
}
for (int p=mxp;p>=0;p--)//枚举购买件数
{
if (a.size()&&a.front()>p-1) a.pop_front();
if (a.size())
{
a2[p*w+b]=max(a2[a.front()*w+b]+v*(p-a.front()),a2[p*w+b]);
}
if (p-c-1<0) continue;//下界超限
k=p-c-1;
while (a.size()&&a2[a.back()*w+b]-a.back()*v<=a2[k*w+b]-k*v) a.pop_back();
a.push_back(k);
}
}
}
wr(a2[m]);
printf("\n");
}
return 0;
}