多重背包
注意考虑下边界和0的大小关系
\[f[i][u+p*v_i]=\max_{max(0,p-c_i) \le k \le p-1 } \{ f[i-1][u+k*v_i]+(p-k)\times w_i \}
\]
边界分析:
所求范围:
\(i\in [1,n],p \in [0,(m-u)/v_i],u\in [0,v_i)\)
递推边界:
\(p \in [1,(m-u)/v_i]\)
所以:
\(p=0\)时 \(f[i][u]=f[i-1][u]\)
i=1时:
若\(u!=0,f=-INF\)
若\(u=0\) 在max里只需要有一个为0即可 所以f[0]=0
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=1010;
const int M=20010;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
int c[N],v[N],w[N],f[M],n,m;
deque<int> q;
int calc(int i,int u,int k){return f[u+k*v[i]]-k*w[i];}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;i++)
v[i]=read(),w[i]=read(),c[i]=read();
memset(f,0xcf,sizeof f); f[0]=0;
//要注意下边界和0的大小的判断
for(int i=1;i<=n;i++)
{
for(int u=0;u<v[i];u++)
{
while(q.size()) q.pop_back();
int maxp=(m-u)/v[i];
for(int k=maxp-1;k>=max(maxp-c[i]+1,0);k--)
{
while(q.size()&& calc(i,u,k) >= calc(i,u,q.back() ) ) q.pop_back();
q.push_back(k);
}
for(int p=maxp;p>=1;p--)
{
while(q.size()&&q.front()>p-1) q.pop_front();
//注意取等号
if(p-c[i]>=0)
{
while(q.size()&&calc(i,u,q.back())<=calc(i,u,p-c[i]) ) q.pop_back();
q.push_back(p-c[i]);
}
//通常来说 只要保证决策集合存在 就不需要判断队列是不是空
f[u+p*v[i]]=max(f[u+p*v[i]],calc(i,u,q.front())+p*w[i]);
}
}
}
int ans=0;
for(int i=1;i<=m;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号