【OD机试题解法笔记】爱吃蟠桃的孙悟空

题目

孙悟空爱吃蟠桃,有一天趁着蟠桃园守卫不在来偷吃。已知蟠桃园有 N 棵桃树,每颗树上都有桃子,守卫将在 H 小时后回来。

孙悟空可以决定他吃蟠桃的速度K(个/小时),每个小时选一颗桃树,并从树上吃掉 K 个,如果树上的桃子少于 K 个,则全部吃掉,并且这一小时剩余的时间里不再吃桃。

孙悟空喜欢慢慢吃,但又想在守卫回来前吃完桃子。

请返回孙悟空可以在 H 小时内吃掉所有桃子的最小速度 K(K为整数)。如果以任何速度都吃不完所有桃子,则返回0。

输入描述
第一行输入为 N 个数字,N 表示桃树的数量,这 N 个数字表示每颗桃树上蟠桃的数量。

第二行输入为一个数字,表示守卫离开的时间 H。

其中数字通过空格分割,N、H为正整数,每颗树上都有蟠桃,且 0 < N < 10000,0 < H < 10000。

输出描述
吃掉所有蟠桃的最小速度 K,无解或输入异常时输出 0。

用例

输入输出

2  3  4  5

4

5

2  3  4  5

3

0

思考

题目大意是要求孙悟空在H小时内以最小速度吃完N棵树上的桃子。

细节:1、一小时只能吃一棵树的桃子,如果提前吃完了,也得等到下一个小时继续吃别的桃子树;2、如果桃子树数量大于守卫离开时长,则不可能吃完所有桃子树,直接返回0。

考虑在桃树数量小于等于守卫离开时长H情形下的最小吃桃速度。吃桃速度取决于桃树的桃子数量。若吃桃速度为数量最大的桃树数量,则一定能吃完所有桃子,而吃桃的最小速度是1个/棵树,如果一颗树一小时吃不完,会导致总的吃桃时长超过桃树数量,而这种情形比较适合桃树数量少于守卫离开时长数。因此目标是在区间[1, max](max是桃树桃子数目最大值)中查找一个最小值且能满足在H时长内吃完所有桃树桃子,可以使用二分查找解决。

算法过程

1、判断桃树数量是否小于守卫离开时长数H,若是直接返回0,无解,否则进入第二步;

2、找到桃子数量最大值Max,在区间[1, Max]进行二分查找,目标找到最小的速度数值满足能在规定时间H内吃完所有桃树;

3、定义一个tryFinishEating函数接受一个速度参数,返回结果是能不能按给定的速度在给定时长数H内吃完所有桃子,定义一个最小速度变量初始化为Max,用于记录最终结果;

4、二分查找序列,不断把速度值向序列左侧边界推进,同时将每轮查找到满足规定时间吃完所有桃树桃子的速度与最小速度变量进行比较更新最小速度。当查找的速度值不满足条件了,记录的最小速度便是最终结果。

可视化

-----------------------------------------------------------后续补充---------------------------------------------------------

参考代码

function solution(lines) {
   const arr = lines.split('\n');
   const H = Number(arr[1].trim());
   const trees = arr[0].trim().split(' ').map(Number);
   
   const maxNum = Math.max(...trees);

   if (trees.length > H) return 0;
   
   function tryCanFinishEating(num) {
      let total = 0;
      for (let n of trees) {
         if (total > H ) return false;
	     total += Math.ceil(n / num);
      }
   }

   let l = 1, r = maxNum, speed = maxNum;
   while (l <= r) {
     let m = l + Math.floor((r -l)/2);
     if (tryCanFinishEating(m)) {
        r = m-1;
	    speed = Math.min(speed, m);
     } else {
        l = m + 1;
     }

   }  

   return speed;
}

let inputs = [
`2 3 4 5
4`,
`2 3 4 5
3`,
];

inputs.forEach(input => {
  console.log(solution(input));
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值