单调队列优化多重背包 洛谷P1776 宝物筛选

题目链接:https://www.luogu.org/problem/P1776

首先我们知道单调队列,就是在区间移动时动态维护区间的最值

单调队列优化的主要思想就是分组更新,因为w[i] w[i]w[i]是成倍增加的

对于当前为w的体积,我们可以按它的余数分w组,即0,1....w-1

同一个余数的在一组

每组的转移是不影响的,也就是说单独转移

f[i][5w]=max(f[i1][4w]4v,f[i1][3w]3v,f[i1][2w]2v,f[i1][w]v,f[i1][0])

f[i][4w]=max(f[i1][3w]3v,f[i1][2w]2v,f[i1][w]v,f[i1][0])

如上所示,每一个都可能有其组内前几个更新来,且不确定是哪个更新的,所以就可以用单调队列优化啦,维护区间的最大值。区间长度就是物品的个数num

原来的状态转移方程为:f[i][j]=max(f[i1][j],f[i1][jkw[i]]+kv[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;
} 
原文地址:https://www.cnblogs.com/qingjiuling/p/11350135.html