当前:LC1011 · 在 D 天内送达包裹的能力 · 首次出现于 Day 47 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC1011 · Capacity To Ship Packages · 二分

在 D 天内送达包裹的能力:运载能力的答案二分

直接求最小能力难,给定能力数天数容易。能力越大天数越少,对能力二分。

cap 的下界是 max(weights)——单件不能拆;上界是 sum(weights)——一天运完。对 mid 做一次按序装载:当天已装 cur,再来一件会超过 mid 就换新的一天。需要的天数 ≤ D 则可行,收高界;否则抬低界。收敛的 lo 就是最小可行能力。时间 O(n log Σw)。

时间 O(n log Σw)空间 O(1)结论先行 · 全文约 6 节
导读

这是 LeetCode 1011. Capacity To Ship Packages Within D Days。传送带上的包裹必须按给定顺序装船,一件不能拆到两天。船的运载能力是每天最多装多少重量。求能在 days 天内运完的最小能力。

主例 weights = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],days = 5。能力 15 时五天分别是 1+2+3+4+5、6+7、8、9、10,刚好 5 天。能力 14 时 1+2+3+4=10,5+6=11,之后 7、8、9、10,要 6 天,不够。答案是 15。

从 1 枚举到总和再逐个检查,能做对,检查次数太多。缺的是可行性关于能力单调:能力变大,天数不会变多。本文先把「给定 cap 要几天」走清楚,再在 [10, 55] 上二分收到 15。

给定能力,按顺序装,数要几天

包裹不能重排、不能拆。给定 cap,唯一合法的装法就是从左到右:当天已经装了 cur,下一件 w 若 cur+w > cap,今天必须收工,w 放到新的一天;否则继续累加。天数从 1 起,每换一天加 1。

这样装出来的天数是这个 cap 下的最少天数——能挤进今天的绝不留到明天。总天数 ≤ D,这个 cap 就可行。

用手走主例。cap=15:1+2+3+4+5=15 满一天;6+7=13;8;9;10。五天,可行。cap=14:1+2+3+4=10,5 再加就 15,换天;5+6=11;7;8;9;10。六天,不可行。单件 10 已经要求 cap 至少为 10,更小的能力连最重那件都装不下。演示用一次模拟说明「按序装满再换天」;真正卡在答案两侧的,是 14 与 15。

为什么按序贪心就够题目禁止打乱顺序。能装进今天而不装,只会让后面某天更挤,天数不会变少。所以「装到装不下为止」就是这个 cap 的最少天数。
能力 10:模拟装包裹,需要 5 天
12345678910
D = 5能力 10需要 5≤ D ✓每天尽量装满:1+2+3+4=10 → 5+... 分 5 天

能力单调,对答案二分

cap 变大,同一套贪心装法只会少换天、不会多换天。存在一个分界:左边不可行,右边全部可行。最小的那个可行值就是答案。搜索区间 [max(w), sum(w)],主例是 [10, 55]。

取 mid,跑一遍 can(mid)。可行则答案在左半,hi=mid;不可行则答案在右半,lo=mid+1。写成 lo+(hi-lo)/2,避免下标相加溢出。

主例收几步。lo=10、hi=55,mid=32,三天就能运完,hi=32。继续收到 mid=15:can(15)=5 ≤ 5,hi=15。再试 14:6 天 > 5,lo=15。lo 与 hi 重合,最小能力 15。演示停在收敛的这两帧:15 可行,区间缩成一点。

能力二分:可行则收高界
12345678910
D = 5[low=15, high=16]mid=15can(15) = 5 天 ≤ 5 → 可行,high=15

Go:答案二分

solution.goGo
func shipWithinDays(weights []int, days int) int {
can := func(cap int) bool {
d, cur := 1, 0
for _, w := range weights {
if cur+w > cap { d++; cur = 0 }
cur += w
}
return d <= days
}
lo, hi := slices.Max(weights), 0
for _, w := range weights { hi += w }
for lo < hi {
mid := lo + (hi-lo)/2
if can(mid) { hi = mid } else { lo = mid + 1 }
}
return lo
}

1can 按序装,返回天数是否 ≤ D。主例 can(15) 为真,can(14) 为假。

2cur+w > cap 才换天,并把 cur 清零后再加上 w。w 本身不会大于 lo 的下界。

3下界是最重一件,上界是总重。主例 10 与 55。

4可行收 hi,不可行抬 lo。最后 lo 就是第一份可行能力。

总结

对能力二分,用按序装载当检查。主例 14 要 6 天,15 要 5 天,答案 15。

  • 不能重排、不能拆件,所以检查函数就是从左到右装满再换天。
  • 能力越大天数越少。二分的是答案本身,不是下标。
  • 下界 max(w)、上界 sum(w)。与 Koko 吃香蕉、分割数组的最大值同一模板。
同族题目
LC875Koko 吃香蕉LC410分割数组的最大值LC704二分查找