[算法题] 二分查找之-爱吃香蕉的珂珂

624 阅读1分钟

一、题目描述:

珂珂喜欢吃香蕉。这里有 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 示例 3:

输入: piles = [30,11,23,4,20], H = 6 输出: 23  

提示:

1 <= piles.length <= 10^4 piles.length <= H <= 10^9 1 <= piles[i] <= 10^9

二、解题思路

二分搜索问题的泛化,四步法 :

  1. 确定变量x 一般就是要求的最小值或最大值
  2. 确定f(x) f是随着x的递增或递减的函数
  3. 确定target f(x)=target约束条件
  4. 确定left,right边界

2.1 递增和递减函数

递增函数:

image.png

递减函数:

image.png

2.2 解题

按步骤来:

2.2.1. 确定x

x就是 每小时吃香蕉的根数: x根/小时。

2.2.2 确定f(x)

看到target是小时,所以f(x)就是吃完所有香蕉的需要的总小时。

   int f(int[] piles,x){
        int hours=0;
        for(int i=0;i<piles.length;i++){
            hours+=piles[i]/x;
            if(piles[i]%x>0){
                hours++;
            }
        }
        return hours;
    }

2.2.3.确定约束条件

target==H。相当于找到等于target的元素。

2.2.4 左右边界确定

left左边界明显为1,有边界,看题目描述: 1 <= piles[i] <= 10^9,因此right取1000000000。当然,因为x表示吃香蕉的根数,它不会超过N堆香蕉的最大根数,所以可以遍历取得piles[i]中的最大值。

注意,f是递减的函数!!

public int minEatingSpeed(int[] piles, int H) {
    int left = 1;
    int right = 1000000000 + 1;
    
    while (left ‹ right) {
        int mid = left + (right - left) / 2;
        if (f(piles, mid) == H) {
            // 搜索左侧边界,则需要收缩右侧边界
            right = mid;
        } else if (f(piles, mid) ‹ H) {
            // 需要让 f(x) 的返回值大一些
            right = mid;
        } else if (f(piles, mid) › H) {
            // 需要让 f(x) 的返回值小一些
            left = mid + 1;
        }
    }
    return left;
}

最终, 因为要求x的最小值,那么其实就是求f(x)==target的左边第一个出现的元素!

所以代码如下:

class Solution {
    public int minEatingSpeed(int[] piles, int h) {

        int left=1;
        int right=1000000000;
        while(left<=right){
            int mid=left+(right-left)/2;
           int cost= eatCostTime(mid,piles);
            if(cost> h){
                //因为是递减函数,所以要让eatCostTime返回更小的值。因此调整的是左边界
                 left=mid+1;
            }else if(cost< h){
                 //因为是递减函数,所以要让eatCostTime返回更大的值。因此调整的是左边界
                right=mid-1;
            }else{
                System.out.println("mid: "+mid);
                if(0==mid||eatCostTime(mid-1,piles)!=h){
                    return mid;
                }else{
                    right=mid-1;
                }
                
            }

        }
        // 没有找到
        return left;

    }

    public int eatCostTime(int x,int[] piles){
        int hour=0;
        for(int i=0;i<piles.length;i++){
            hour+=piles[i]/x;
            if(piles[i]%x>0){
                hour++;
            }
        }
        return hour;
    }
}

三 原题连接:

珂珂喜欢吃香蕉