疯狂的背包问题(10) - 多重背包问题 III
多重背包模板题,数据极大需用单调队列优化,按体积余数分组,滑动窗口维护最优前驱状态,O(NV)。
OJ: luogu
题目 ID: U663797
难度:提高
标签:动态规划多重背包单调队列背包
日期: 2026-08-08 23:11
题意
有
思路
一句话本质:按体积取模分组后,每组内转移在等差数列下标上进行,形成滑动窗口最大值问题,用单调队列做到
二进制分组够快吗?
不够。每种物品拆出约
直接看转移方程,dependence 是什么样的?
对于第
这个依赖结构提示什么?
把
分完组后,转移变成什么形式?
令组内下标为
把
这个式子像什么?
对于
怎么
单调队列维护递减的
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* 多重背包问题 III — 单调队列优化
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50005;
int n, V;
int dp[maxn], g[maxn];
int q[maxn], head, tail;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> V;
for (int i = 1; i <= n; ++i) {
int v, w, s;
cin >> v >> w >> s;
memcpy(g, dp, sizeof(dp));
for (int r = 0; r < v; ++r) {
head = 0, tail = 0;
for (int k = 0; r + k * v <= V; ++k) {
int idx = r + k * v;
int val = g[idx] - k * w;
while (head < tail && q[head] < k - s) ++head;
while (head < tail && g[r + q[tail - 1] * v] - q[tail - 1] * w <= val) --tail;
q[tail++] = k;
dp[idx] = g[r + q[head] * v] + (k - q[head]) * w;
}
}
}
cout << dp[V] << '\n';
return 0;
}复杂度
- 时间:
。每组余数内每个位置入队出队各一次,每件物品均摊线性于 。 - 空间:
(dp 数组 + 备份数组 g + 单调队列)。
总结
多重背包从