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


    传送门


    多重背包

    学了这么多年oi竟然只知道二进制拆分做法,白学了
    多重背包就是每个物品有个数限制的01背包。

    怎么做呢?

    最暴力的是把每个物品拆成m[i]个物品,做01背包。
    这样时间显然会爆炸。

    二进制拆分优化

    于是可以利用二进制性质,拆的时候打个包。
    对于每种物品来说,每 (1,2,4…2^k) 个物品看做一个物品,最后再加上 (m-2^{k+1}) 这个物品,一共组成了 (k+1) 个物品。
    而这 (k+1) 个数字正好能组成 (1)(m) 的所有数字。
    于是就成了log的复杂度。

    单调队列优化

    这个log也是可以避免的。
    这里用到单调队列优化。
    我们发现对于给定的一个物品i,其体积大小w是确定的,所以对于每一个枚举的容量j,一定是从 (dp[j-k imes w]) 转移过来,其中k的范围为 (0<=k<=m[i])
    于是我们发现,可以把 j 按照 j%w 的值进行分类,即在转移时一定是余数相同的j之间进行转移。
    这样对于每一个余数开一个单调队列,就可以快乐地使用单调队列优化了。

    AC代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<iomanip>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int maxn=100005;
    long long ans,dp[maxn],v[maxn],w[maxn],m[maxn],n,maxm;
    int main(){
    	ios::sync_with_stdio(false);
    	cin>>n>>maxm;
    	for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>m[i];
    	for(int i=1;i<=n;i++){
    		for(int d=0;d<w[i];d++){
    			deque<int> q;
    			for(int k=0;k<=m[i];k++){
    				int j=maxm-d-k*w[i];
    				if(j<0) break;
    				while(!q.empty()&&dp[q.back()]-v[i]*(q.back()/w[i])<dp[j]-v[i]*(j/w[i])) q.pop_back();
    				q.push_back(j);
    			}
    			for(int j=maxm-d;j>=0;j-=w[i]){
    				while(!q.empty()&&j-m[i]*w[i]>=0&&dp[q.back()]-v[i]*(q.back()/w[i])<dp[j-m[i]*w[i]]-v[i]*((j-m[i]*w[i])/w[i])) q.pop_back();
    				if(j-m[i]*w[i]>=0) q.push_back(j-m[i]*w[i]);
    				dp[j]=max(dp[j],dp[q.front()]+(j-q.front())/w[i]*v[i]);
    				while(!q.empty()&&q.front()>=j) q.pop_front();
    			}
    		}
    	}
    	for(int i=1;i<=maxm;i++) ans=max(ans,dp[i]);
    	cout<<ans;
        return 0;
    }
    
  • 相关阅读:
    经典javascript
    大话prototype
    DataTable使用方法总结
    实验四 Web服务器1socket编程
    2.4 OpenEuler中C语言中的函数调用测试
    20191323王予涵第13章学习笔记
    20191323王予涵第十三章学习笔记
    2.5 OpenEuler 中C与汇编的混合编程
    个人贡献
    20191323王予涵第十二章学习笔记
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15224740.html
Copyright © 2020-2023  润新知