每天一道 leetcode——875. 爱吃香蕉的珂珂

875. 爱吃香蕉的珂珂

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例 1:

输入: piles = [3,6,7,11], H = 8
输出: 4
示例 2:

输入: piles = [30,11,23,4,20], H = 5
输出: 30
注意:
    1 <= piles.length <= 10^4
    piles.length <= H <= 10^9
    1 <= piles[i] <= 10^9

分析:二分查找法
先给piles 排序,最小速度 K 肯定在 piles 的 最小堆数量和最大堆数量之间,即 min_piles[0] <= k <= min_piles[n]
采用二分查找的方法,mid = (right - left) /2 + left
如果 k = mid 在 H 小时能吃完所有香蕉,则 继续探索 k 的范围,k 可能更小,如果缩小范围后 k 不满足,则返回上一个 k 符合要求的值。

因为piles.length 和 piles[i] 的长度 和大小是有限的,所以结果不用对piles 数组排序,直接把二分查找范围限定在 1 ~ 10^9,执行时间会更快。

public class lc_875_minEatingSpeed {

    private int flag ;
    public int minEatingSpeed(int [] piles,int H){

        int n = piles.length;
//        Arrays.sort(piles);

        if( H == n) return piles[n-1];
        if(H > n && n == 1){
            return piles[0] % H == 0 ? piles[0]/H :  piles[0] / H + 1 ;
        }
        int left = 1,right = 1_000_000_000;
        while (left < right){
            int mid =(right - left)/2 + left;
            //检车 每小时吃 mid 个,是否能吃完
            if(check(mid,piles,H)){
                flag = mid;
                right = mid;
            }
            //如果不能吃完
            else{
                left = mid + 1;
            }
        }
        //判断跳出循环的 left 是否符合,不符合的话,就返回最近的上一个符合的值
        return check(left,piles,H) ? left:flag;

    }

    private boolean check(int k, int[] piles,int H) {
        int count = 0;
        for(int pile:piles){
           count += (pile - 1 )/k + 1;
        }
        return count <= H;
    }


}

总结: 采用二分查找,和本题相似的还有
1011. 在 D 天内送达包裹的能力
410. 分割数组的最大值

1011. 在 D 天内送达包裹的能力
  1. 在 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) 是不允许的。
1 <= D <= weights.length <= 50000
1 <= weights[i] <= 500
public class lc_1011_shipWithinDays {
    public int shipWithinDays(int[] weights, int D) {

        int n = weights.length;
        if (D == 1) {
            int res = 0;
            for (int val : weights) {
                res += val;
            }
            return res;
        }
        int left = 1, right = Integer.MAX_VALUE;
        while (left < right) {
            int mid = (right - left) / 2 + left;
            //判断在最低载重量为 mid 时,能否在 D 天内送达
            if (check(weights, D, mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;

    }


    private boolean check(int[] weights, int D, int K) {
        int cur = K;
        int days = 1;
        for (int weight : weights) {
            if (weight > K)
                return false;
            if (cur < weight) {
                cur = K;
                days++;
            }
            cur -= weight;
        }

        return days  <= D;

    }
}

总结:分割数组的最大值这道题

将数组分成 m 个连续子数组 == D 天送包裹这题的D 天内把所有包裹送到另一个港口
这 m 个子数组各自和的最大值最小 ==能在 D 天内将传送带上的所有包裹送达的船的最低运载能力
但是一道 mid,一道却是 hard,所以有时候不要被题目的背景给吓到了,多分析内部的联系。
笔试题编程题多是背景复杂,题目描述一大串,认真读题就好啦。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值