单调队列优化DP
步骤
- 加入所需元素:向单调队列重复加入元素直到当前元素达到所求区间的右边界,这样就能保证所需元素都在单调队列中.
- 弹出越界队首:单调队列本质上是维护的是所有已插入元素的最值,但我们想要的往往是一个区间最值.于是我们弹出在左边界外的元素,以保证单调队列中的元素都在所求区间中.
- 获取最值:直接取队首作为答案即可.
单调队列优化多重背包
问题
有 \(k\) 种物品,每种价值为 \(v_i\),重量为 \(w_i\),个数为 \(x_i\);总容量为 \(W\),求最大价值
解析
容易得到转移方程 \(f_{ij}={max} ^{k_i}_{k=0} f_{i-1,j-k*w_i}+k*v_i \space (j-k+w_i\ge0)\)
但这样不方便转移,因此我们可以将方程转变为
\[f_{i,j+k*w_i}={max}^{k}_{p=0} f_{i-1,j+p*w_i}+p*v_i-k*v_i \space(j+k*w_i\le W,k-p\le k_i)
\]
这样就可以用单调队列维护一个长度为 \(k_i\),元素大小为 \(f_{i-1,j+p*w_i}+p*v_i\) 的滑动窗口
for (int i = 1; i <= n; i++) {
for (int y = 0; y < w[i]; y++) {
// 清空队列
deque<int>().swap(q);
for (int x = 0; x * w[i] + y <= W; x++) {
// 弹出不在范围的元素
while (!q.empty() && q.front() < x - k[i]) q.pop_front();
// 保证队列单调
while (!q.empty() && f[last][q.back() * w[i] + y] - q.back() * v[i] < f[last][x * w[i] + y] - x * v[i]) q.pop_back();
q.push_back(x);
f[now][x * w[i] + y] = f[last][q.front() * w[i] + y] - q.front() * v[i] + x * v[i];
}
}
swap(last, now);
}

浙公网安备 33010602011771号