”二分“高频题型总结:最小最大值、最大最小值、满足条件最小 / 最大

二分核心:具有一定的单调性 / 可分段判定 → 二分枚举答案 ,再用check函数验证可行性。国赛 90% 二分不是裸查找数组,是二分找答案 + check 函数

国赛二分基本就 4 种考法:最小最大值、最大最小值、满足条件最小 / 最大、实数二分。

目录

[一、经典 1:最大值最小化](#一、经典 1:最大值最小化)

典型例题:分割木板

[二、经典 2:最小值最大化](#二、经典 2:最小值最大化)

典型例题:距离分配问题

[三、经典 3:满足条件的最大 x(反向临界)](#三、经典 3:满足条件的最大 x(反向临界))

典型例题:切绳子

[四、二分 + 前缀和(子数组 / 区间和高频)](#四、二分 + 前缀和(子数组 / 区间和高频))

五、二分易错点

总结


一、经典 1:最大值最小化

常见例如:分成 k 段 / 选 k 个物品,求分割后的最大值尽可能小

模板思路

复制代码
l=下界,r=上界
while(l<r):
    mid=(l+r)/2
    if check(mid): r=mid
    else: l=mid+1
最后l就是答案

check(x):判断 x 能不能满足划分要求

典型例题:分割木板

给定 n 块木板长度an,切成 k 段,每段连续,求分割后最长一段的最小长度

题意:n 块木板按序排放连成一整条,只能在木板缝隙切(不能把单块木板劈两半),一共切 k−1 刀,分成 k 段(每段含有连续若干块原木板),让这「k 段里最长的那一段尽可能短」,求最长的那段的最小值。

理清题目要我们干什么:

  • 只能从木板之间分割、不能切开单块木板,所以 任意一段长度 ≥ 数组里单个木板最大值
  • 必须恰好分成 k 个连续段
  • 每种分割方案都有一个「最长段的长度」
  • 在所有合法分割方案里,找出最小的那个最长段的值(答案)

解题套路:二分→猜答案

目标:找到 最小的 X:按原顺序切木板,分成 ≤k 段,每段的总和≤X

X的取值范围:

  • 下界(最小) left=max(a)(不能切碎木板,最长的那段最少装一块最大木板)
  • 上界(最大) (不切,一整个就一段)
  • 二分 mid(在取值范围的两端取了一个中间值,作为X):检验如果每段不超过 mid,最少需要切成几段 cnt
    • cnt<=k:说明 mid 可以再缩小一点,试一下更小答案 right=mid,因为题目中就是要最小的X。(就比如:现在是mid是4,最长的段长是4,假设分成4,4可分成两段,如果mid=2,最长的段长是2,就可以分成4段了,更压缩了,就可以分更多段。分成更多段,意味着更均匀,更细化,求最小的max(段长))

    • cnt>k:不满足要求k段,说明mid太小了,需要变大 left=mid+1

      import java.util.*;
      public class Main {
      static int[] a;
      static int k;

      复制代码
      //校验mid是否合法
      static boolean check(long mid) {
          int cnt = 1; //初始1段
          long sum = 0;
          for (int x : a) {
              if (sum + x > mid) {  //加超了,说明需要在这里分割了,这个x需要算成下一段的开端,让sum=x,继续往后累加
                  cnt++;
                  sum = x;
              } else sum += x;
          }
          return cnt <= k;
      }
      
      public static void main(String[] args) {
          Scanner sc = new Scanner(System.in);
          int n = sc.nextInt();
          k = sc.nextInt();
          a = new int[n];
          long L = 0, R = 0;
          for (int i = 0; i < n; i++) {
              a[i] = sc.nextInt();
              L = Math.max(L, a[i]);
              R += a[i];
          }
          //整数二分模板
          while (L < R) {
              long mid = (L + R) / 2;
              if (check(mid)) R = mid;
              else L = mid + 1;
          }
          System.out.println(L);
      }

      }


二、经典 2:最小值最大化

典型例题:距离分配问题

题目:n 个牛棚的坐标 xn 升序,放 m 头牛,每头牛不在同一棚,求两头牛最小间距的最大值(X)

思路:还是用二分法去求那个X

  1. L=1,R=xn-1-x0 (相距最远)
  2. check (mid):贪心放牛,第一个位置必放,下一个坐标 - 上次坐标≥mid 才放,统计能放多少头牛
    • 能放的数量≥m:mid 可行,尝试更大的距离 L=mid+1,保存答案
    • 放不下,说明相距的最小距离太大了,需要把值调小:R=mid-1

代码实现

复制代码
import java.util.*;
public class Main {
    static int[] x;
    static int m;

    static boolean check(int dis) {
        int cnt = 1;
        int last = x[0];
        for (int i = 1; i < x.length; i++) {
            if (x[i] - last >= dis) {
                cnt++;
                last = x[i];
            }
        }
        return cnt >= m;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        m = sc.nextInt();
        x = new int[n];
        for (int i = 0; i < n; i++) x[i] = sc.nextInt();
        Arrays.sort(x);
        int L = 1, R = x[n - 1] - x[0];
        int ans = 0;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (check(mid)) {
                ans = mid;
                L = mid + 1;
            } else R = mid - 1;
        }
        System.out.println(ans);
    }
}

三、经典 3:满足条件的最大 x(反向临界)

x 越小越容易满足,找最大合法 x。例:在限定数量下小车最多载重

复制代码
mid=(l+r+1)/2
if check(mid):l=mid
else:r=mid-1

向上取整 mid 避免死循环是国赛易错点

典型例题:切绳子

注:绳子可以切断,多余的废料可以不要,只留下满足要求的,不同于分木板

题目:n 根绳子长度 an ,裁成 k 段等长小段,小段长度保留 2 位小数,求可以切的最长长度。

思路:

  • 实数二分:L=0,R=max(a),循环 100 次(精度 1e-12 足够)
  • check (len):每根绳子能切 ai / len 向下取整,总和≥k 则可行

代码实现:

复制代码
import java.util.*;
public class Main {
    static double[] a;
    static int k;

    static boolean check(double len) {
        int cnt = 0;
        for (double x : a) cnt += (int)(x / len);
        return cnt >= k;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        k = sc.nextInt();
        a = new double[n];
        double L = 0, R = 0;
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextDouble();
            R = Math.max(R, a[i]);
        }
        //实数二分,循环100次保证精度
        for (int t = 0; t < 100; t++) {
            double mid = (L + R) / 2;
            if (check(mid)) L = mid;
            else R = mid;
        }
        System.out.printf("%.2f", L);
    }
}

四、二分 + 前缀和(子数组 / 区间和高频)

题目:给定数组an正数,找连续子数组 和≥S 的最小长度

a=2,3,1,2,4,3,S=7 →答案 2(4,3

思路:

要求最小长度,就二分长度len,check函数:滑动窗口 / 前缀和,是否存在区间长度 = len,区间和≥S;

check函数:子数组和/区间和用前缀和实现

  • 存在:尝试更小长度 R=mid
  • 不存在:L=mid+1

代码实现:

复制代码
import java.util.*;
public class Main {
    static int[] a;
    static int S;

    static boolean check(int len) {
        long sum = 0;
        //初始化第一个窗口
        for (int i = 0; i < len; i++) sum += a[i];
        if (sum >= S) return true;
        //滑动窗口
        for (int i = len; i < a.length; i++) {
            sum += a[i] - a[i - len];
            if (sum >= S) return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        S = sc.nextInt();
        a = new int[n];
        for (int i = 0; i < n; i++) a[i] = sc.nextInt();
        int L = 1, R = n;
        int ans = n;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (check(mid)) {
                ans = mid;
                R = mid - 1;
            } else L = mid + 1;
        }
        System.out.println(ans);
    }
}

五、二分易错点

  1. 求最小:mid=(l+r)/2check→r=mid
  2. 求最大:mid=(l+r+1)/2check→l=mid
  3. 实数二分不能用整数边界条件,循环 100 次
  4. l、r 初始化范围要拉满(不要卡小,防止答案出界)

总结

题目要求什么的最值X,就二分谁,check函数把mid值传进去,在mid的前提下根据题目中的约束条件看是否满足题目的要求。然后做对应的left or right的调整

  1. 整数二分:最小化可行值(分木板模板)

    while(L < R){
    long mid = L+(R-L)/2; //防溢出
    if(check(mid)) R=mid;
    else L=mid+1;
    }
    //答案L

  2. 整数二分:最大化可行值(放牛模板)

    while(L <= R){
    int mid = L+(R-L)/2;
    if(check(mid)){ans=mid; L=mid+1;}
    else R=mid-1;
    }
    //答案ans

  3. 实数二分模板(循环 100 次)

    for(int i=0;i<100;i++){
    double mid=(L+R)/2;
    if(check(mid)) L=mid;
    else R=mid;
    }

相关推荐
地平线开发者3 小时前
Transformer模型部署之性能优化指南
算法
地平线开发者3 小时前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶
半个落月6 小时前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
小月土星7 小时前
JavaScript 快速排序:从 pivot、双指针到分治思想
javascript·算法·面试
小月土星7 小时前
JavaScript 递归入门:从 1 到 n 求和,再到数组扁平化
javascript·算法·面试
To_OC1 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
鱼鱼不愚与1 天前
《原来如此 | 第01期:为什么导航软件能预测红绿灯倒计时?》
算法
复杂网络1 天前
论最小 Agent 计算机的形态
算法
kisshyshy2 天前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法