【板子】二分答案(最大最小?)

一、什么题目要用「二分答案」?

当你看到题目问的是**"最大的最小"** 、"最小的最大" ,或者**"满足条件的最小值/最大值"**时,优先考虑二分答案。

典型关键词:

  • 最大值最小化(如:最小距离的最大值)

  • 最小值最大化(如:最大装载量的最小值)

  • 第 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 段内完成
}

六、用法思路总结

  1. 读题,找关键词

    • "最小的最大"、"最大的最小"
  2. 确认答案具有单调性

    • 若答案变大,可行性只会变差或不变
  3. 确定二分区间 l, r

  4. 写出 check(mid)

    • 贪心 / 模拟 / DP
  5. 套用对应模板

  6. 调试边界(整数二分最容易错)

ps

当然面向奇怪的数据可以进行枚举答案(doge)

当题目要求是找到综合多维的值:比如,正常情况,给多段绳子,可以砍一定刀,求最大长度最短是标准的二分答案,但是如果题目是要求:段越小价值指数下降,求所有绳子代价最小的切法,这时候用二分答案确实可以找到最长段最短,但是达到这个节点依旧可能剩余刀数,求代价最小应该切完所有刀。。。此时只能用代价优先队列dp

:代价优先队列dp也能直接解二分答案大部分题目

优先队列贪心dp-CSDN博客

相关推荐
优化Henry4 小时前
BBU与RRU光链路详解:从双纤一收一发到单纤双向
网络·笔记·学习·5g·tdd
yi0114 小时前
DAY17: LeetCode 139|单词拆分:从“把单词删掉”到用 DP 记录合法切口
人工智能·笔记·python·算法·leetcode·动态规划
陈希瑞4 小时前
LongCat-2.5-Preview 的web逆向能力实测
人工智能·算法·wasm
垆边人似月.5 小时前
华为机考题:二叉树的最大路径和
c语言·c++
2601_962218615 小时前
深入理解C++中的thread_local线程局部变量的应用
开发语言·c++
无名猿5 小时前
C++ lambda 捕获列表详解:值捕获、引用捕获、初始化捕获与悬垂引用
c++·现代c++·语法基础·踩坑记录
sunoo-2295 小时前
【嵌入式 ARM 学习第十二天】SPI 裸机驱动 + I2C 触摸屏驱动 + 三大串行协议深度对比
arm开发·笔记·vscode·学习·imx6ull
tangguofeng6 小时前
基于分治策略的循环赛日程表问题的思路讲解
算法
AOI小白新手上路6 小时前
4-1_地址空间_RISC与CISC_学习笔记
笔记·学习
起个名字费劲死了6 小时前
VisionMaster集成深度学习算法(基础狗)
人工智能·深度学习·算法