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
    }
}

 

posted @ 2020-10-17 18:07  星海寻梦233  阅读(175)  评论(0编辑  收藏  举报