题目链接:https://www.luogu.org/problem/P1776
首先我们知道单调队列,就是在区间移动时动态维护区间的最值
单调队列优化的主要思想就是分组更新,因为w[i] w[i]w[i]是成倍增加的
对于当前为w的体积,我们可以按它的余数分w组,即0,1....w-1
同一个余数的在一组
每组的转移是不影响的,也就是说单独转移
f[i][5w]=max(f[i−1][4w]−4v,f[i−1][3w]−3v,f[i−1][2w]−2v,f[i−1][w]−v,f[i−1][0])
f[i][4w]=max(f[i−1][3w]−3v,f[i−1][2w]−2v,f[i−1][w]−v,f[i−1][0])
如上所示,每一个都可能有其组内前几个更新来,且不确定是哪个更新的,所以就可以用单调队列优化啦,维护区间的最大值。区间长度就是物品的个数num
原来的状态转移方程为:f[i][j]=max(f[i−1][j],f[i−1][j−k∗w[i]]+k∗v[i])
现在的状态转移方程为:f[i][j]=max(f[i-1][d+j*w]-j*v,f[i-1][j])+a*v.(a=W/w)
即原方程减的越少,等价于现在这个方程加的越多,所以区间可以从头移动到尾。(原方程选择数目是从左开始,对应现方程就是从右开始)
看的几篇博客:背包九讲详解版
https://blog.csdn.net/qq_40679299/article/details/81978770
https://www.cnblogs.com/-guz/p/9866118.html
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; const int inf=1<<30; const int maxn=4e4+7; int dp[maxn],q[maxn],p[maxn]; int main(){ int n,W;cin>>n>>W; int ans=0; for(int i=1;i<=n;i++){ int v,w,num;scanf("%d%d%d",&v,&w,&num); if(w==0)ans+=v*num; num=min(W/w,num);//最大能够承载的该物品的数量 for(int d=0;d<w;d++){//枚举重量的余数 int head=0,tail=0; int k=(W-d)/w;//我们转换后的表达式为dp[d+k*w]-k*v,所有我们要保证d+k*w<=W for(int j=0;j<=k;j++){ while(head<tail&&dp[d+j*w]-j*v>=q[tail-1])tail--; q[tail]=dp[d+j*w]-j*v;p[tail++]=j; while(head<tail&&p[head]<j-num)head++; dp[d+j*w]=max(dp[d+j*w],q[head]+j*v); } } } cout<<dp[W]+ans<<endl; return 0; }