一、什么题目要用「二分答案」?
当你看到题目问的是**"最大的最小"** 、"最小的最大" ,或者**"满足条件的最小值/最大值"**时,优先考虑二分答案。
典型关键词:
-
最大值最小化(如:最小距离的最大值)
-
最小值最大化(如:最大装载量的最小值)
-
第 k 大的值最小是多少?
-
能否在 x 时间内完成?
-
答案具有单调性(这是核心!)
经典例题:
-
POJ 2456 / AcWing 102:Aggressive cows(奶牛放牛栏,最近距离最大)
-
POJ 1064:Cable master(切绳子,最长长度)
-
洛谷 P2678:跳石头(移走最少石头,最短跳跃距离最大)
-
洛谷 P1182:数列分段 Section II(最多分成 m 段,最大段和最小)
-
各种"最小值最大"、"最大值最小"的贪心+验证题
二、核心思想
不直接求最优解,而是"猜"一个答案 mid,判断这个答案是否可行(check(mid))。
如果可行,说明答案可以更大(或更小),往一侧继续二分。
三、整数二分答案板子
模板 1:求「最小值最大」(最大化最小值)
常见:最近距离最大、最少时间最少、最大承载量最小等
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> a;
// check函数:判断答案 mid 是否可行
bool check(int mid) {
// 根据题意写贪心/DP验证逻辑
// 返回 true 表示 mid 可行,可以尝试更大的
// 返回 false 表示 mid 太大,需要缩小
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 读入数据...
int l = 0, r = 1e9; // 根据题目确定上下界
while (l < r) {
int mid = l + (r - l + 1) / 2; // 右中点(防死循环)
if (check(mid)) {
l = mid; // mid 可行,尝试更大的
} else {
r = mid - 1; // mid 不可行,缩小
}
}
cout << l << "\n";
return 0;
}
模板 2:求「最大值最小」(最小化最大值)
常见:最大段和最小、最少分组数最多等
int l = 0, r = 1e9;
while (l < r) {
int mid = l + (r - l) / 2; // 左中点
if (check(mid)) {
r = mid; // mid 可行,尝试更小的
} else {
l = mid + 1; // mid 太小,需要增大
}
}
cout << l << "\n";
四、浮点数二分答案板子
用于精度要求高的题目(如长度、重量、时间)。
double l = 0.0, r = 1e9;
const double eps = 1e-6; // 根据题目要求调整,通常 1e-6 ~ 1e-8
while (r - l > eps) {
double mid = (l + r) / 2.0;
if (check(mid)) {
l = mid; // 或 r = mid,看题意
} else {
r = mid;
}
}
printf("%.6f\n", l);
浮点二分不用纠结边界,直接 l=mid / r=mid 即可
五、check 函数怎么写?(最关键)
**check(mid)** 的本质:
给定一个"候选答案",问:能不能在这个限制下完成任务?
常见套路:
-
贪心:能选就选,不能选就跳过
-
计数:统计满足条件的组数、段数、操作次数
-
模拟:模拟整个过程,判断是否合法
示例:数列分段(最大段和最小)
题意:把 n 个数分成 m 段,每段和尽量小,求最大段和的最小值。
bool check(int mid) {
int cnt = 1; // 当前段数
int sum = 0; // 当前段的和
for (int i = 0; i < n; i++) {
if (sum + a[i] <= mid) {
sum += a[i];
} else {
cnt++;
sum = a[i];
}
}
return cnt <= m; // 能否在 m 段内完成
}
六、用法思路总结
-
读题,找关键词
- "最小的最大"、"最大的最小"
-
确认答案具有单调性
- 若答案变大,可行性只会变差或不变
-
确定二分区间 l, r
-
写出 check(mid)
- 贪心 / 模拟 / DP
-
套用对应模板
-
调试边界(整数二分最容易错)
ps
当然面向奇怪的数据可以进行枚举答案(doge)
当题目要求是找到综合多维的值:比如,正常情况,给多段绳子,可以砍一定刀,求最大长度最短是标准的二分答案,但是如果题目是要求:段越小价值指数下降,求所有绳子代价最小的切法,这时候用二分答案确实可以找到最长段最短,但是达到这个节点依旧可能剩余刀数,求代价最小应该切完所有刀。。。此时只能用代价优先队列dp
:代价优先队列dp也能直接解二分答案大部分题目