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

二分核心:具有一定的单调性 / 可分段判定 → 二分枚举答案 ,再用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;
    }

相关推荐
罗超驿1 小时前
14.LeetCode 438 题解:滑动窗口+哈希表找所有字母异位词
java·算法·leetcode
小欣加油1 小时前
leetcode239 滑动窗口最大值
数据结构·c++·算法·leetcode·哈希算法
luoganttcc1 小时前
FP16 和 BF16 的数学表达
算法
玖釉-1 小时前
Vulkan 示例解析:pipelines.cpp 如何在一个 Render Pass 中切换多条 Graphics Pipeline
c++·windows·算法·图形渲染
ji198594432 小时前
局部线性嵌入(LLE)算法 MATLAB 实现
算法·机器学习·matlab
Deepoch2 小时前
Deepoc VLA开发板:无人机群体协同与无网络自主作业核心
网络·人工智能·算法·无人机·deepoc·具身模型开发板
随意起个昵称2 小时前
线性dp-计数类题目11(不等数列)
c++·算法·动态规划
Black蜡笔小新2 小时前
自动化AI算法训练服务器DLTM零代码私有化部署筑牢企业AI落地根基
人工智能·算法·自动化
wWYy.2 小时前
算法:最大子数组和
算法