题目描述
传送带上的包裹必须在D
天内从一个港口运送到另一个港口。
传送带上的第i个包裹的重量为weights[i]
。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。
返回能在D
天内将传送带上的所有包裹送达的船的最低运载能力。
样例
输入:weights = [1,2,3,4,5,6,7,8,9,10], D = 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) 是不允许的。
输入:weights = [3,2,2,4,1,4], D = 3
输出:6
解释:
船舶最低载重 6 就能够在 3 天内送达所有包裹,如下所示:
第 1 天:3, 2
第 2 天:2, 4
第 3 天:1, 4
输入:weights = [1,2,3,1,1], D = 4
输出:3
解释:
第 1 天:1
第 2 天:2
第 3 天:3
第 4 天:1, 1
数据范围
1 <= D <= weights.length <= 50000
1 <= weights[i] <= 500
算法1
(二分) $O(nlog(Σw))$
思路:为什么能想到二分。首先根据y总的给出的根据数据范围反推算法,该题中的weights
数组长度为50000以内
,而当n≤100000 => O(nlogn)
可以推出各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、spfa、求凸包、求半平面交、二分
、CDQ分治、整体二分。
继续分析,满足该题目的船体载重在什么范围。
船体的最小载重一定是最大的 weights
,最大载重一定是sum(weights)
。可以这么理解因为 天数D
在[1,len(weights)]
之间,所以当D
为1时,答案是最大载重;当D
等于len(weights)
时,答案是最小载重。当D
在(1, len(weights))
之间时,答案则在最小载重与最大载重之间。
此时答案数组已经有序,并且左右区间都满足同样的性质,故可二分。
时间复杂度:因为二分的区间为[max(weights),sum(weights)]
,所以二分查找需要执行的次数为$O(log(Σw))$,每一次二分都要遍历完整个weights
数组,时间为$O(n)$,所以最后的时间复杂度为$O(nlog(Σw))$。
空间复杂度:只使用了几个常量,所以空间复杂度为$O(1)$。
python 代码
class Solution:
def shipWithinDays(self, weights: List[int], D: int) -> int:
l, r = max(weights), sum(weights)
while l < r:
mid = l + r >> 1
# 计算运输天数
days = 0
cur_weight = 0
for w in weights:
if cur_weight + w <= mid:
cur_weight += w
else:
days += 1
# cur_weight应该初始化为w,否则当次w会被抛弃。
cur_weight = w
# days还没算上最后一天,所以这里不能"<="
if days < D:
r = mid
else:
l = mid + 1
return r
今日重点
先来回顾一下二分的模板代码
# 定义边界
l, r = 0, n
whlie l < r:
# 当满足check()条件时,若时l = mid, 则前面的mid赋值记得加1。
mid = l + r >> 1
if check(mid):
r = mid
else:
l = mid + 1
二分算法需要的条件:在check(mid)
函数中,只要满足mid左边的区间都满足一定的性质,mid右边的区间也满足一定的性质即可二分。如本题所示,mid含义为船的载重,则mid满足条件时,mid的右区间一定也都满足将货物在指定天数都送达的能力;当mid不满足条件时,则mid的左区间也都不会满足将货物在指定天数内都送达的能力。
ps.1