yyy2015c01 的 U 盘
二分最小文件大小限制L,每次check用0/1背包判断在容量S限制下能否装下价值≥p的文件。
OJ: luogu
题目 ID: P2370
难度:普及+/提高-
标签:动态规划01背包二分答案
日期: 2026-08-08 23:13
题意
有
问在满足条件的前提下,最小的接口大小 No Solution!。
思路
一句话本质:“最小的最大文件大小” 是典型的二分答案模型:二分
题目有多个变量纠缠在一起——文件大小限制、容量限制、价值目标,应该如何拆解?
先抓住主要矛盾:如果我们确定了接口大小
既然
二分范围 check(mid) 判断是否可行。可行就尝试更小的 r = mid-1),否则加大 l = mid+1)。
check(L) 内部怎么做?
遍历所有物品,跳过
最后检查是否存在某个
二分的边界如何取?
如果 ans == -1 就输出 No Solution!。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXS = 1005;
int n;
ll p;
int S;
int w[MAXS];
int v[MAXS];
// dp[j] 是 check() 函数内的局部 DP 数组,表示用容量 j 能获得的最大价值。
ll dp[MAXS];
// 判断在文件大小不超过 L 的情况下,能否装下价值 ≥ p 的文件。
bool check(int L) {
fill(dp, dp + S + 1, 0);
for (int i = 1; i <= n; i++) {
if (w[i] > L) continue;
for (int j = S; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
for (int j = 0; j <= S; j++) {
if (dp[j] >= p) return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> p >> S;
int max_w = 0;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
max_w = max(max_w, w[i]);
}
// 二分答案:找最小的文件大小限制 L 使得可以装下 ≥ p 的价值。
int l = 1, r = max_w, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
if (ans == -1) {
cout << "No Solution!" << '\n';
} else {
cout << ans << '\n';
}
return 0;
}复杂度
- 二分次数:
- 每次 check:
- 总复杂度:
,在 下可行 - 空间复杂度:
总结
这道题的真正难点不是背包本身,而是把"最小化接口大小"翻译成二分答案 + 可行性验证。当答案有单调性且验证相对简单时,二分答案是最自然的降维思路:把"求最优 L"拆成"判断某个 L 是否可行"。