在 D 天内送达包裹的能力

标签: 数组 二分查找

难度: Medium

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

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

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

示例 1:

输入:weights = [1,2,3,4,5,6,7,8,9,10], days = 5
输出:15
解释:
船舶最低载重 15 就能够在 5 天内送达所有包裹,如下所示:
第 1 天:1, 2, 3, 4, 5
第 2 天:6, 7
第 3 天:8
第 4 天:9
第 5 天:10

请注意,货物必须按照给定的顺序装运,因此使用载重能力为 14 的船舶并将包装分成 (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) 是不允许的。 

示例 2:

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

示例 3:

输入:weights = [1,2,3,1,1], days = 4
输出:3
解释:
第 1 天:1
第 2 天:2
第 3 天:3
第 4 天:1, 1

提示:

  • 1 <= days <= weights.length <= 5 * 104
  • 1 <= weights[i] <= 500

Submission

运行时间: 111 ms

内存: 20.4 MB

class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -> int:
        l, r = max(weights), max(weights) * ceil(len(weights) / days)
        def check(cap):
            ship = 0
            day = 1
            for w in weights:
                ship += w
                if ship > cap:
                    day += 1
                    ship = w
                    if day > days:
                        return False
            return True

        while l < r:
            m = (l + r) // 2
            if check(m):
                r = m
            else:
                l = m +1
        return l

Explain

该题解采用二分查找法确定最小的运载能力。首先设置二分查找的范围:下界 l 是 weights 中的最大值(因为载重至少要能承载单个包裹中的最大重量),上界 r 是按最大包裹权重均匀分布时的总重量。然后定义一个辅助函数 check(cap),用于判断给定的载重 cap 是否能在指定的 days 天内运送完所有包裹。具体方法是顺序累加包裹重量,当累加的重量超过 cap 时,需要使用新的一天继续装载,如果使用的天数超过 days,则返回 False。通过二分查找逐渐缩小可能的载重范围,直到找到最小的符合条件的载重。

时间复杂度: O(n log(R-L))

空间复杂度: O(1)

python
class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -> int:
        l, r = max(weights), sum(weights)  # 设置搜索范围
        def check(cap):
            ship = 0  # 当前船的载重
            day = 1  # 当前使用的天数
            for w in weights:
                ship += w
                if ship > cap:  # 如果当前包裹加载后超过最大载重,需要使用新的一天
                    day += 1
                    ship = w  # 从新的一天开始,当前包裹是第一个装载的
                    if day > days:  # 如果使用的天数超过了限制,返回 False
                        return False
            return True  # 如果所有包裹都可以在限定天数内装载完,返回 True

        while l < r:  # 二分查找最小可能载重
            m = (l + r) // 2
            if check(m):  # 如果当前载重可以满足条件,尝试更小的载重
                r = m
            else:
                l = m + 1
        return l  # 返回最小载重

Explore

在此问题中,下界设置为weights数组中的最大值是因为任何有效的运载能力至少应该能够承载单个包裹的最大重量。如果运载能力小于weights中的最大值,那么至少有一个包裹无法被运载,因此这样的载重能力不符合问题的基本需求。设置下界为weights中的最大值是为了确保所有包裹都可以被单独运载,从而满足基本的逻辑要求。

当check函数返回False时,说明当前的运载能力cap(即mid值)不足以在指定的天数内完成所有包裹的运送。因此,需要尝试一个更大的运载能力来满足条件。通过设置l = m + 1,我们排除了当前和更小的运载能力值,将搜索范围向可能成功的更大值方向调整。这是二分搜索策略的一部分,旨在逐步缩小搜索范围,直至找到最小的满足条件的运载能力。

check函数在处理完所有weights后直接返回True实际上已经考虑了所有边界情况。函数逻辑确保在不超过指定天数的情况下,每一天的累积载重都不会超过cap。只要最后一天的累积载重没有超过cap,无论是刚好达到还是略低于最大载重,都表明这个cap值是足够的。此函数保证了在给定的天数内,所有包裹都可以被运送完毕,因此可以直接返回True。

在实际应用中,通常会通过事先的设备选择和规划来确保船只的最大承载能力至少等于或大于预计中单个包裹的最大重量。这通常涉及到对船只的承载标准的了解和包裹重量的预估。在运输前的准备阶段,会有明确的规定或者检查,以确保每个包裹的重量都在船只的承载能力范围内。如果存在任何包裹超过船只最大承载能力的风险,那么需要重新评估包裹的分配或者选择更大承载能力的船只。