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[i−j∗w]+j∗v)
接着设
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+j∗w]−j∗v)+(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;
}
有点意思