luogu P1776 宝物筛选_NOI导刊2010提高(02)(单调队列优化多重背包)

luogu P1776 宝物筛选_NOI导刊2010提高(02)(单调队列优化多重背包)

大意

单调队列优化多重背包

首先对于多重背包有一种优化方式是01二进制拆分,然后用01背包来做
但有个log,还是不够优秀
于是就有了单调队列优化多重背包
v : val       w : weight     s:个数
首先可以滚掉一维,设g为上一行的状态
则可得最朴素的转移方程: 这里 i 表示容量大小
f [ i ] = m a x ( g [ i − j ∗ w ] + j ∗ v ) f[i] = max(g[i - j * w] + j * v) f[i]=max(g[ijw]+jv)

接着设 d = i     m o d     w ; d = i\ \ \ mod \ \ \ w; d=i   mod   w;
则可得
f [ i ] = m a x ( g [ d + j ∗ w ] − j ∗ v ) + ( i / w ) ∗ v f[i] = max(g[d + j * w] - j * v) + (i / w) * v f[i]=max(g[d+jw]jv)+(i/w)v 这里 ( i / w ) (i / w) (i/w)是下取整
然后发现可以按照d分类,然后这个转移方程就可以单调队列优化了

code:

#include<bits/stdc++.h>
#define calc(x) (g[d + x * w] - x * v)
#define int long long
using namespace std;
int n, m, f[100005], q[100005], ans, g[100005];
signed main(){
   scanf("%lld%lld", &n, &m);
   for(int i = 1; i <= n; i ++){
   	int v, w, s;
   	scanf("%lld%lld%lld", &v, &w, &s);
   	if(w == 0) ans += v * s;//注意
   	for(int d = 0; d < w; d ++){
   		int l = 1, r = 0;
   		for(int j = 0; j * w + d <= m; j ++){
   			while(l <= r && q[l] + s < j) l ++;//如果用的次数超过s就弹出
   			while(l <= r && calc(q[r]) < calc(j)) r --;//更新
   			q[++ r] = j;
   			f[d + j * w] = max(f[d + j * w], calc(q[l]) + j * v);//转移
   		}
   	}
   	for(int j = 0; j <= m; j ++) g[j] = f[j];//记得这里
   }
   printf("%lld", ans + f[m]);//输出
   return 0;
}

有点意思

第100篇blog祭

posted @ 2019-08-22 21:45  lahlah  阅读(23)  评论(0编辑  收藏  举报