二分查找题目:在 D 天内送达包裹的能力

题目

标题和出处

标题:在 D 天内送达包裹的能力

出处:1011. 在 D 天内送达包裹的能力

难度

5 级

题目描述

要求

传送带上的包裹必须在 days \texttt{days} days 天内从一个港口运送到另一个港口。

传送带上的第 i \texttt{i} i 个包裹的重量为 weights[i] \texttt{weights[i]} weights[i]。每一天,我们都会按给出重量( weights \texttt{weights} weights)的顺序往传送带上装载包裹。我们装载的重量不能超过船的最大运载重量。

返回能在 days \texttt{days} days 天内将传送带上的所有包裹送达的船的最低运载能力。

示例

示例 1:

输入: weights   =   [1,2,3,4,5,6,7,8,9,10],   days   =   5 \texttt{weights = [1,2,3,4,5,6,7,8,9,10], days = 5} weights = [1,2,3,4,5,6,7,8,9,10], days = 5
输出: 15 \texttt{15} 15
解释:船舶最低载重 15 \texttt{15} 15 就能够在 5 \texttt{5} 5 天内送达所有包裹,如下所示:
1 \texttt{1} 1 天: 1,   2,   3,   4,   5 \texttt{1, 2, 3, 4, 5} 1, 2, 3, 4, 5
2 \texttt{2} 2 天: 6,   7 \texttt{6, 7} 6, 7
3 \texttt{3} 3 天: 8 \texttt{8} 8
4 \texttt{4} 4 天: 9 \texttt{9} 9
5 \texttt{5} 5 天: 10 \texttt{10} 10
请注意,货物必须按照给定的顺序装运,因此使用载重能力为 14 \texttt{14} 14 的船舶并将包装分成 (2,   3,   4,   5),   (1,   6,   7),   (8),   (9),   (10) \texttt{(2, 3, 4, 5), (1, 6, 7), (8), (9), (10)} (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) 是不允许的。

示例 2:

输入: weights   =   [3,2,2,4,1,4],   days   =   3 \texttt{weights = [3,2,2,4,1,4], days = 3} weights = [3,2,2,4,1,4], days = 3
输出: 6 \texttt{6} 6
解释:船舶最低载重 6 \texttt{6} 6 就能够在 3 \texttt{3} 3 天内送达所有包裹,如下所示:
1 \texttt{1} 1 天: 3,   2 \texttt{3, 2} 3, 2
2 \texttt{2} 2 天: 2,   4 \texttt{2, 4} 2, 4
3 \texttt{3} 3 天: 1,   4 \texttt{1, 4} 1, 4

示例 3:

输入: weights   =   [1,2,3,1,1],   days   =   4 \texttt{weights = [1,2,3,1,1], days = 4} weights = [1,2,3,1,1], days = 4
输出: 3 \texttt{3} 3
解释:
1 \texttt{1} 1 天: 1 \texttt{1} 1
2 \texttt{2} 2 天: 2 \texttt{2} 2
3 \texttt{3} 3 天: 3 \texttt{3} 3
4 \texttt{4} 4 天: 1,   1 \texttt{1, 1} 1, 1

数据范围

  • 1 ≤ days ≤ weights.length ≤ 5 × 10 4 \texttt{1} \le \texttt{days} \le \texttt{weights.length} \le \texttt{5} \times \texttt{10}^\texttt{4} 1daysweights.length5×104
  • 1 ≤ weights[i] ≤ 500 \texttt{1} \le \texttt{weights[i]} \le \texttt{500} 1weights[i]500

解法

思路和算法

为方便表述,下文使用「最低运载能力」表示能在 days \textit{days} days 天内将传送带上的所有包裹送达的船的最低运载能力。

如果实际运载能力大于等于最低运载能力,则将传送带上的所有包裹送达需要的天数小于等于 days \textit{days} days;如果实际运载能力小于最低运载能力,则将传送带上的所有包裹送达需要的天数大于 days \textit{days} days。因此,这道题是二分查找判定问题,需要找到最低运载能力。

low \textit{low} low high \textit{high} high 分别表示二分查找的下界和上界。由于同一个包裹必须在一次内运载,不能分成多次运载,因此最低运载能力不能小于最重的包裹, low \textit{low} low 的初始值等于 weights \textit{weights} weights 中的最大值;由于至少运载一次,装载的总重量最大为所有包裹的重量之和,因此 high \textit{high} high 的初始值等于 weights \textit{weights} weights 中的元素之和。

为了能在规定的天数内将传送带上的所有包裹送达,当运载能力确定时,每次装载应在不超过运载能力的前提下将装载的重量最大化。由于必须按照给定的顺序装载包裹,因此当给定包裹重量数组和运载能力时,可以遍历数组计算将传送带上的所有包裹送达需要的天数。计算方法是遍历数组 weights \textit{weights} weights 计算包裹重量之和,如果包裹重量之和大于运载能力则将最后一个包裹移到后一天的装载中,遍历结束时即可得到总天数。

每次查找时,取 mid \textit{mid} mid low \textit{low} low high \textit{high} high 的平均数向下取整,将 mid \textit{mid} mid 作为运载能力,判断是否可以在 days \textit{days} days 天内将传送带上的所有包裹送达,执行如下操作。

  • 如果以运载能力 mid \textit{mid} mid 可以在 days \textit{days} days 天内将传送带上的所有包裹送达,则最低运载能力小于等于 mid \textit{mid} mid,因此在 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中继续查找。

  • 如果以运载能力 mid \textit{mid} mid 不能在 days \textit{days} days 天内将传送带上的所有包裹送达,则最低运载能力大于 mid \textit{mid} mid,因此在 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中继续查找。

low = high \textit{low} = \textit{high} low=high 时,查找结束,此时 low \textit{low} low 即为最低运载能力。

实现方面,由于只需要判断是否可以在 days \textit{days} days 天内将传送带上的所有包裹送达,因此如果遍历尚未结束时总天数就已经大于 days \textit{days} days,则一定不能在 days \textit{days} days 天内将传送带上的所有包裹送达,此时可以提前返回,不需要计算总天数。

代码

class Solution {
    public int shipWithinDays(int[] weights, int days) {
        int low = Arrays.stream(weights).max().getAsInt(), high = Arrays.stream(weights).sum();
        while (low < high) {
            int mid = low + (high - low) / 2;
            if (canShip(weights, days, mid)) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return low;
    }

    public boolean canShip(int[] weights, int days, int capacity) {
        int totalDays = 1;
        int totalWeight = 0;
        int length = weights.length;
        for (int i = 0; i < length; i++) {
            int weight = weights[i];
            if (totalWeight + weight <= capacity) {
                totalWeight += weight;
            } else {
                totalDays++;
                if (totalDays > days) {
                    return false;
                }
                totalWeight = weight;
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ s ) O(n \log s) O(nlogs),其中 n n n 是数组 weights \textit{weights} weights 的长度, s s s 是数组 weights \textit{weights} weights 中的元素之和。需要执行 O ( log ⁡ s ) O(\log s) O(logs) 次二分查找,每次二分查找需要 O ( n ) O(n) O(n) 的时间遍历数组 weights \textit{weights} weights 并计算将传送带上的所有包裹送达的总天数,时间复杂度是 O ( n log ⁡ s ) O(n \log s) O(nlogs)

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值