题面
原题过于繁复,这里口述,多重背包,多次询问,每次询问的背包容量不一样,并且每次询问会规定一个物品不能选(这个规定的物品每次询问都会改变)。
题解
首先考虑对询问的两个要求,容量不一样很好处理,因为DP跑背包的过程中本来就会记录不同容量时的答案。
对于规定一个物品不能选怎么处理呢?不计算这一个物品,需要计入答案的物品就被分成了编号比这个物品小的物品集合和编号比这个物品大的物品集合,可以直接双向跑背包,f表示从前到后的,g表示从后到前的,然后把两个背包合并。
因为这是个多重背包,还需要套一个二进制拆分。
#include<bits/stdc++.h>
using namespace std;
namespace fastio{
template<typename tn> void read(tn &a){
tn x=0,f=1;char c=' ';
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar() ) x=x*10+c-'0';
a=x*f;
}
template<typename tn> void print(tn a){
if(a<0) putchar('-'),a=-a;
if(a>9) print(a/10);
putchar(a%10+'0');
}
};
using namespace fastio;
int n,q;
const int N=2e4+5;
const int M=1e3+5;
int a,b,c;
int w[N],v[N],cnt;
int f[N][M],g[N][M];
int l[N],r[N];
int main(){
read(n);
for(int i=0;i<n;i++){
read(a),read(b),read(c);
for(int j=1;j<=c;j<<=1){
c-=j;
v[++cnt]=j*a,w[cnt]=j*b;
}
if(c) v[++cnt]=c*a,w[cnt]=c*b;
l[i]=cnt+1;
r[i]=l[i-1]-1;
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<M;j++){
if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
else f[i][j]=f[i-1][j];
}
for(int i=cnt;i>=1;i--)
for(int j=1;j<M;j++)
if(j>=v[i]) g[i][j]=max(g[i+1][j],g[i+1][j-v[i]]+w[i]);
else g[i][j]=g[i+1][j];
read(q);
while(q--){
int d,e;
read(d);
read(e);
int ans=0;
for(int i=0;i<=e;i++)
ans=max(ans,f[r[d]][i]+g[l[d]][e-i]);
printf("%d\n",ans);
}
return 0;
}