标签:洛谷 int front P1776 maxn && 物品 include dp
传送门
多重背包
学了这么多年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\times 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;
}
标签:洛谷,int,front,P1776,maxn,&&,物品,include,dp 来源: https://www.cnblogs.com/yinyuqin/p/15224740.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。