疯狂的背包问题(9) - 多重背包问题 II
多重背包模板题,数据范围扩大(N,V,s≤1000),需用二进制分组将每种物品拆分成 O(log s) 个 01 物品。
OJ: luogu
题目 ID: U663791
难度:普及+/提高
标签:动态规划多重背包二进制优化背包
日期: 2026-08-08 23:11
题意
有
思路
一句话本质:用二进制分组把每种物品从
上一题的三重循环为什么不够?
问题的本质是什么?
原来的做法把
怎么用更少的包拼出
二进制表示告诉我们:任何一个整数都可以唯一表示为若干个
- 第
个包包含 件原物品,体积 ,价值 - 最后若有余数
,再补一个包
例如
为什么这样分组后与原问题等价?
原问题允许选
于是算法变为:对每种物品进行二进制拆分,每个包当作一个独立的 01 物品跑标准 01 背包。
代码
/**
* 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
* 多重背包问题 II — 二进制分组优化
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2005;
int n, V;
int dp[maxn];
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;
for (int k = 1; s > 0; k <<= 1) {
int cnt = min(k, s);
s -= cnt;
int pack_v = cnt * v;
int pack_w = cnt * w;
for (int c = V; c >= pack_v; --c)
dp[c] = max(dp[c], dp[c - pack_v] + pack_w);
}
}
cout << dp[V] << '\n';
return 0;
}复杂度
- 时间:每种物品拆出
个包,总物品数 ,01 背包 。 - 空间:
。
总结
二进制分组是多重背包最经典的优化,核心思想是把"选几个"的问题等价转化为"选哪些二进制包"的问题,把物品数量从