leetcode 875:爱吃香蕉的珂珂
1.问题描述:
测试数据:
2.先验知识:
这道题是一道很经典的二分应用的题目。在介绍如何解决这道题之前,我首先抛出我平时习惯两套二分模板,只需要在return时稍做修改,便可以解决90%以上的二分问题,相当好用。下面给出的例子中默认在逻辑上是升序排序的,如果在实际应用中遇到降序问题我们只需要稍作思考修改代码即可。
#第一种:当check为true时,结果落在左侧区间,此时我们找到的是arr中第一个大于等于target的数。
def biselect_left(l,r,arr,target):
#l 是 二分的左边界
#r 是 二分的右边界
while l < r:
mid = l + r >> 1
if check(mid) >= target:
r = mid
else:
l = mid + 1
return l
_____________________________________________________________________________________
#第二种:当check为true时,结果落在右侧区间,此时我们找到的是arr中第一个小于等于target的数。
def biselect_right(l,r,arr,target):
#l 是 二分的左边界
#r 是 二分的右边界
while l < r:
mid = l + r + 1 >> 1
if check(mid) <= target:
l = mid
else:
r = mid - 1
return l
3.思路:
当我们读完了上述两个模板时,其实就可以解决大部分比较简单的二分问题了,具体问题请自行前往leetcode二分学习计划入门级别中练习。值得注意的一点是,我习惯了平时将二分区间mid写作直接除以2,但有时会因为数组太大产生越界问题,请各位在入门时养成好习惯:l + (r - l) / 2就不会产生越界了。
好了,言归正传,当我第一眼看到这道题,什么?这鬼问题能用二分解决?没错,相信和我一样刚刚入门算法的人一定会被这样的想法震撼到,我们学到的二分是什么,使用条件我可以像背课文一样背出来:有序数组~。没错,确实是有序,但是我们似乎被数学思想禁锢了,2 > 1 是有序的,它为什么是有序的,是因为我们知道在数值上2确确实实大于1,可我要问你为什么 arr[2]> arr[1],也许你会觉得我这是一个非常弱智的问题,我tm从小学就知道2 > 1,数组是升序的,当然arr[2] > arr[1]了。非常好,相信如果到这里你能懂的话,你马上就能理解这道题为什么能用二分了。上文我提到了,在逻辑上是升序的。我们是将下标对数据抽象成了一个哈希映射函数,下标对应哈希位置的值。
重点来了:
这道题我们的目标是找到一个吃香蕉的最小速度k,使得吃完香蕉的最小时间小于等于h。让我们思考一下,这里target相当于h,我们要找到一个自变量k,使得f(k) <= h,这里f代表吃完香蕉时间的函数,我们可以发现,其是单调的,所以和之前提到的哈希映射是一个道理。那么,开始无脑二分。等等等等,在二分之前,我们有两个模板啊,我们应该选择哪个模板呢?来思考一下,我们要找到一个最小的k,使得f(k) <= h,也就是我们要在时间的数轴上找到一个小于等于h的数,那当然是选择第一个了。ok,相信讲到这里,你应该能明白这道题为什么能选择二分以及具体应该怎么做了。接下来看看python的具体实现,其他语言殊途同归,我就不多赘述了。
4.具体实现:
class Solution:
def minEatingSpeed(self, piles: List[int], h: int) -> int:
def f(piles,k):
total_hour = 0
for pile in piles:
total_hour += pile//k
if pile % k != 0:
total_hour += 1
return total_hour
#速度相当于数据下标,f(k)相当于哈希映射
l = 1
r = 1000000100 #题目给定的h范围
#接下来就是套模板
while l < r:
mid = l + r >> 1
if f(piles,mid) <= h:
r = mid
else:
l = mid + 1
return l
总结:
这道题其实如果见过这个思路的话是很容易想到的,但是如果没见过确实还是比较困难的,所以特此记录一下,另附我混在今年秋招群中看到的某厂的面试原题,请各位自品。
那么就到此结束了,下次作为小白的我有了更深的感悟再见,感谢各位的观看,欢迎各位来指出错误讨论。