浅析二分算法 | 豆包MarsCode AI刷题

77 阅读3分钟

二分算法是一种高效的查找与优化方法,常用于解决搜索范围明确的优化问题。本篇文章以一道使用二分算法解决的题目为例,一步步推导出如何使用二分解决该问题,并总结出使用到二分算法的题目的一些特征,帮助大家掌握如何灵活运用二分算法解题。


问题描述

小R需要在 days 天内将一批包裹从一个港口运送到另一个港口。传送带上的每个包裹的重量由数组 weights 表示,第 i 个包裹的重量为 weights[i]

每一天,小R按照包裹在 weights 中的顺序装载包裹,装载的总重量不会超过船的最大运载能力。为了在规定的天数内完成运输任务,小R希望知道船的最低运载能力是多少,才能确保所有包裹能够在 days 天内全部送达。


测试样例

样例1:

输入:weights = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1],days = 5
输出:15

样例2:

输入:weights = [3, 2, 2, 5, 1, 4],days = 2
输出:10

样例3:

输入:weights = [1, 2, 3, 8, 4],days = 4
输出:8

解法

思路分析

问题要求找到最低的运载能力,使得所有包裹在 days 天内运送完成。为此,我们需要探索运载能力 capacity 的取值范围,并设计一个判定方法来检验某一运载能力是否可行。

核心约束

  1. 包裹必须按顺序装载,不能改变顺序。
  2. 每天装载的总重量不能超过船的运载能力。

问题可以转化为:在给定的运载能力下,是否可以在 days 天内完成运输。


二分算法设计

运载能力的最低值为 max(weights)(单个包裹的最大重量),因为船至少要能运送最重的包裹;最高值为 sum(weights)(所有包裹的重量之和),对应将所有包裹一次运送。在上述范围内,通过二分查找确定最低的可行运载能力。

定义一个函数 check(weights, days, capacity)

  • 初始化天数计数 cnt = 1 和当日总重量 cur = 0
  • 遍历包裹重量数组:
    • 若当前包裹重量 w 加上 cur 不超过 capacity,则将其加入当前天。
    • 否则,分配到下一天,cnt++
  • 检查 cnt 是否超过 days,若超过则返回 false,否则返回 true

代码实现

public class Main {

    public static boolean check(int[] weights, int days, int capacity) {
        int cur = capacity;
        int cnt = 1;
        for (int w : weights) {
            if (w > cur) {
                cur = capacity - w;
                cnt++;
            } else {
                cur -= w;
            }
        }
        return cnt <= days;
    }

    public static int solution(int[] weights, int days) {
        int l = 1, r = 0;
        for (int w : weights) {
            l = Math.max(w, l);
            r += w;
        }
        int mid;
        while (l < r) {
            mid = (l + r) >> 1;
            if (check(weights, days, mid))
                r = mid;
            else
                l = mid + 1;
        }
        // System.out.println(r);
        return r;
    }
}

二分算法的特点

通过本题分析,我们总结能够被二分算法解决的问题的几个特点:

1. 单调性

二分算法通常适用于具有某种单调性的问题:

  • 若增加某个参数使条件成立,则更大的参数也必定成立(或反之)。例如,在本题中,运载能力越大,越容易在规定天数内完成运输。

2. 明确的搜索范围

问题的解空间需要明确、有限。

  • 在本题中,运载能力的搜索范围为 [max(weights), sum(weights)]

3. 判定函数

需要设计一个逻辑清晰的判定函数,用于验证某一搜索值是否满足题目条件:

  • 在本题中,check 判定在给定运载能力下是否可行。