[二分查找] 875. 爱吃香蕉的珂珂

124 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

每日刷题 2022.06.07

题目

  • 珂珂喜欢吃香蕉。这里有 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

解题思路

暴力做法

  • 分析:
    • 珂珂需要在警察回来之前(h小时内)吃掉所有的香蕉
    • 并且h小时内,需要以最慢的速度k将全部香蕉🍌吃完
    • 还需要注意⚠️:如果当前的堆中的香蕉小于k根,那么吃完堆中的香蕉后,这个小时内就不会再吃了。(也就是多了会剩出来,少了不能添,每一堆香蕉并不能合并处理,只能一堆堆的
  • 遍历piles数组,依次去尝试珂珂在h小时内吃掉所有的香蕉的最小速度k,k的范围:1 ~ max(数组中的最大值)
  • 理解清楚了题意之后,就比较简单了。那么就每次判断当前这堆香蕉🍌对于吃香蕉的速度k,最多能够几个小时处理完。将所有的小时加起来就是吃完所有的香蕉需要的时间。

二分查找+排序(学习大佬的解题思路)

  • 经典的二分查找题目
  • 因为根据题意可知:每堆香蕉之间都是相互独立的,且每堆香蕉最小的吃完时间1(当其小于等于K的时候),由此可推出pile[i] / k就是每个香蕉堆需要的时间,注意:这里需要向上取整,因为不足的还需要1小时来完成。
  • 判断是否能够二分:需要有二段性,本题可以二分k,因为k是递增的,且
    • 小于K的值,总时间total > h
    • 大于K的值,总时间一定total <= h
  • 那么剩下就需要确定二分的范围:
    • 当堆里的香蕉小于k的时候,需要使用1小时
    • 当堆里的香蕉大于k,且为数组中最大的元素值,那么k取比其大的元素值都是没有用的,因为k = max(piles[i])等价,因为我们要找最小的K
  • 因此,二分的范围就是1 ~ 数组中最大的元素值
  • 其中的比较check函数,只需要每次计算当前的K是否小于等于h即可,因为我们要找的是等于h时的最小的k,因此右边是<=,左边是>,那么只需要返回右边,就可以找到第一个最小的等于h的。

AC代码

/**
 * @param {number[]} piles
 * @param {number} h
 * @return {number}
 */
var minEatingSpeed = function(piles, h) {
  // 在 h 小时内吃掉所有香蕉的最小速度 k(k为整数)
  // k表示在在每一个小时内能够吃掉的香蕉数
  // 先考虑恰巧能够在规定时间内吃完所有香蕉的,k
  //从小到大进行排序
  piles.sort((a, b) => {
    return a - b;
  })
  const n = piles.length;
  function check(mid) {
    let re = 0;
    // 传递过来的就是:具体的值
    // 计算所需要的小时数
    let tt = 0;
    for(let i = 0; i < n; i++) {
      // 对于每一堆香蕉,珂珂吃完的最长时间
      tt += Math.ceil(piles[i] / mid);
    }
    // return n + Math.ceil(re / mid);
    return tt;
  }
  // 二分查找
  let l = 0, r = piles[n - 1] + 1;
  while(l + 1 != r) {
    let mid = l + parseInt((r - l) / 2);
    let t = check(mid);
    // 升序排序得到的
    if(t <= h) {
      r = mid;
    }else {
      l = mid;
    }
  }
  // 最终的r就是等于h且最小的k
  return r;
};