1011.在 D 天内送达包裹的能力
传送带上的包裹必须在 D 天内从一个港口运送到另一个港口。
传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。
返回能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。
示例 1:
输入: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) 是不允许的。
示例 2:
输入:weights = [3,2,2,4,1,4], D = 3
输出:6
解释:
船舶最低载重 6 就能够在 3 天内送达所有包裹,如下所示:
第 1 天:3, 2
第 2 天:2, 4
第 3 天:1, 4
思路:时间复杂度 O(N*logN):
• 利用 二分查找 的方式,得到每一次尝试的运力:
○ 二分查找的左边界 leftNum:每一次必须完整的将一个货物运过去,所以,最小的运力为数组 weights 中最大的值,这样才能保证每一次都完整的运送货物!
○ 二分查找的右边界 rightNum:所有货物的总重量之和 Sum.
○ 每一次利用二分查找得到运力 mid,用这个 mid 去求解,是否能在D天运送成功;如果能运送成功,则缩小运力;否则,增大运力。
○ 尽量不要调用系统函数,因为有中断开销,所以比较耗费时间。
代码:
class Solution { public int shipWithinDays(int[] weights, int D) { int leftNum = 0, rightNum = 0; for(int weight : weights){ leftNum = weight > leftNum ? weight : leftNum; //如果改为调用系统函数 Math.max(leftNum, weight) 则执行结果的时间从 90% 降为 22% 了 rightNum += weight; //所有物品的总重量 } while(leftNum <= rightNum){ int mid = (leftNum + rightNum) / 2; if(function(weights, D, mid)) rightNum = mid - 1; //如果当前的运力足够在D天运完包裹,则尝试将运力再减小 else leftNum = mid + 1; //否则,增大运力 } return leftNum; //最终,满足条件的最小运力 } private boolean function(int[] weights, int D, int K){ //K:运载能力 int cur = K; //当前剩余运力 for(int weight : weights){ if(weight > cur){ D--; //天数减一 cur = K - weight; } else cur -= weight; //当天的运力减少 } return D > 0 ; //最后一次的 D-- 不会执行,所以 D > 0 } }