各位题友大家好! 今天是 @负雪明烛 坚持日更的第 87 天。今天力扣上的每日一题是「1011. 在 D 天内送达包裹的能力」。

解题思路

题意:给出了一个数组 weights,给出了一个数字 D,求满足最小的承载量的船,能让 weights 在最多 D 天内全部运走。需要注意的是不能交换 weights 中的顺序。

思路:题目要求船的最小承载量,我们就把船的承载量作为变量。遍历所有可行的船的承载量,找出最小承载量。

船的承载量有范围要求的,首先,由于货物不能拆分,所以船的承载量必须大于等于货物中的最大重量;其次,如果船的承载量大于等于所有货物的重量之和,那么所有货物可以在一天时间内全部拉走,所以船的承载量一定会小于等于所有货物的重量之和。故 max(weights) <= 船的承载量 <= sum(weights)

找出船的承载量范围之后,我们思考怎么寻找能在 D 天内能把所有货物都拉走的最小承载量。对于船的承载量为 x 时,由于必须按顺序装载 weights ,所以依次遍历 weights,尽可能能装更多的货,直到这一船装不下了新的货物;从而来计算需要多少天能把所有货物拉完。对于每个 x 计算货物拉完的天数时,需要把所有的 weights 遍历一遍,时间复杂度是 $O(weights)$。

那么怎么寻找最小的船承载量 x 呢?上面我们已经分析了 max(weights) <= 船的承载量 <= sum(weights),如果在这个区间内逐一遍历并判断肯定是不可以的,那样的时间复杂度会过高。肯定会超时。

我们发现这不就是个查找问题么?在指定空间内,查找满足一定条件的解。我们可以用二分查找

分享二分查找的模板:

  1. public int search(int[] nums, int left, int right, int target) {
  2. while (left < right) {
  3. // 选择中位数时下取整
  4. int mid = left + (right - left) / 2;
  5. if (check(mid)) {
  6. // 下一轮搜索区间是 [mid + 1, right]
  7. left = mid + 1
  8. } else {
  9. // 下一轮搜索区间是 [left, mid]
  10. right = mid
  11. }
  12. }
  13. // 退出循环的时候,程序只剩下一个元素没有看到。
  14. // 视情况,是否需要单独判断 left(或者 right)这个下标的元素是否符合题意
  15. }


这个题目,其实就把上面的 check(mid)换成当船的承载量是 mid 时,能否在 D 天内运走所有的货物。

至此,思路已经非常明白了,代码如下。

  1. class Solution(object):
  2. def shipWithinDays(self, weights, D):
  3. l = max(weights)
  4. r = sum(weights)
  5. # [l, r)
  6. while l < r:
  7. mid = l + (r - l) / 2
  8. if self.getNeedDays(weights, mid) > D:
  9. l = mid + 1
  10. else:
  11. r = mid
  12. return l
  13. def getNeedDays(self, weights, x):
  14. need = 1
  15. cur = 0
  16. for w in weights:
  17. if cur + w > x:
  18. need += 1
  19. cur = 0
  20. cur += w
  21. return need
  • 时间复杂度:$O(len(weights) * log(sum(weights)))$。
  • 空间复杂度:$O(1)$。

刷题心得

二分查找,其实不单单是在一个有序数组上能用,要开拓思路。

参考资料:AlgoWiki

相似题目:
875. 爱吃香蕉的珂珂


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

祝大家 AC 多多,Offer 多多!我们明天再见!