vlambda博客
学习文章列表

震惊!这些题也能用二分查找做

网上有很多二分查找的模版,但其实弄明白原理之后并不需要背模版。

首先就是数组有序,最常见的是数组的值从小到大排序。但有些特殊情况的题,二分法的判定条件不是简单的大小关系,而是可以抽离出子函数的情况,这也是今天想重点讨论的题。
说回最常见的情况:数组值已经从小到大排序。
首先是循环判断条件 while left <= right 退出循环时 left != right,可以确定的是 nums[mid] < target时,left = mid + 1, nums[mid] > target时,right = mid - 1。nums[mid] == target时要根据情况来判断,如果题目中有重复值,对于最后要返回的值,那么每次等于target的时候,就要留在结果里。如果是小于等于target值的最大值,在等于target的时候,就应该让right = mid - 1,缩小右边区间。

if nums[mid] < target: left = mid + 1else:      right = mid - 1




1482. Minimum Number of Days to Make m Bouquets

Given an integer array bloomDay, an integer m and an integer k.

We need to make m bouquets. To make a bouquet, you need to use k adjacent flowers from the garden.

The garden consists of n flowers, the ith flower will bloom in the bloomDay[i] and then can be used in exactly one bouquet.

Return the minimum number of days you need to wait to be able to make m bouquets from the garden. If it is impossible to make m bouquets return -1.


Example 1:

Input: bloomDay = [1,10,3,10,2], m = 3, k = 1
Output: 3
Explanation: Let's see what happened in the first three days. x means flower bloomed and _ means flower didn't bloom in the garden.
We need 3 bouquets each should contain 1 flower.
After day 1: [x, _, _, _, _] // we can only make one bouquet.
After day 2: [x, _, _, _, x] // we can only make two bouquets.
After day 3: [x, _, x, _, x] // we can make 3 bouquets. The answer is 3.

Example 2:

Input: bloomDay = [1,10,3,10,2], m = 3, k = 2
Output: -1
Explanation: We need 3 bouquets each has 2 flowers, that means we need 6 flowers. We only have 5 flowers so it is impossible to get the needed bouquets and we return -1.

Example 3:

Input: bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3
Output: 12
Explanation: We need 2 bouquets each should have 3 flowers.
Here's the garden after the 7 and 12 days:
After day 7: [x, x, x, x, _, x, x]
We can make one bouquet of the first three flowers that bloomed. We cannot make another bouquet from the last three flowers that bloomed because they are not adjacent.
After day 12: [x, x, x, x, x, x, x]
It is obvious that we can make two bouquets in different ways.

Example 4:

Input: bloomDay = [1000000000,1000000000], m = 1, k = 1
Output: 1000000000
Explanation: You need to wait 1000000000 days to have a flower ready for a bouquet.

Example 5:

Input: bloomDay = [1,10,2,9,3,8,4,7,5,6], m = 4, k = 2
Output: 9

 

Constraints:

  • bloomDay.length == n

  • 1 <= n <= 10^5

  • 1 <= bloomDay[i] <= 10^9

  • 1 <= m <= 10^6

  • 1 <= k <= n



这道题直接考虑dfs的话,即便用memorization会超时

所以我们从天数考虑,我们可以得到在d天可以形成的花束数量吗?是的,我们可以运行一次简单的计数,方法是检查开花的当天是否小于或等于d,以获取相邻的大于或等于k的花朵的数量。


class Solution: def minDays(self, bloomDay: List[int], m: int, k: int) -> int: if m*k > len(bloomDay): return -1
def getBouq(day): result = 0 count = 0 for i in range(len(bloomDay)): if bloomDay[i] <= day: count += 1 else: count = 0 if count == k: result += 1 count = 0 return result
left = min(bloomDay) right = max(bloomDay) while left <= right: mid = left + (right-left)//2 if getBouq(mid) < m: left = mid + 1 else: right = mid - 1 return left


774. Minimize Max Distance to Gas Station

On a horizontal number line, we have gas stations at positions stations[0], stations[1], ..., stations[N-1], where N = stations.length.


Now, we add K more gas stations so that D, the maximum distance between adjacent gas stations, is minimized.


Return the smallest possible value of D.


Example:

Input: stations = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], K = 9

Output: 0.500000


Note:

stations.length will be an integer in range [10, 2000].

stations[i] will be an integer in range [0, 10^8].

K will be an integer in range [1, 10^6].

Answers within 10^-6 of the true value will be accepted as correct.




首先要明白这题不是贪心,比如

[10, 19, 25, 27, 56, 63, 70, 87, 96, 97],K = 3

其两两之间的距离为:

9,6,2,29,7,7,17,9,1

如果按照贪心,会先将29分开,变成两个14.5,然后会将17分开,变成两个8.5,还剩一个加油站,会将其中一个14.5分开,变成两个7.25。但是这样弄下来,最大的距离还是14.5,而实际上我们有更好的办法,我们用两个加油站将29三等分,会变成三个9.67,然后用剩下的一个去分17,得到两个8.5,此时最大距离就变成了9.67,这才是最优的解法。


这题如果用dp,时间复杂度是O(NK^2), N 是 stations的长度

但是如果用二分查找,时间复杂度就是O(NlogW),其中 N 是 stations 的长度,W = 10^14(W是所有可能答案的范围10^8/10^-6)


问题转化为:

有 k 个加油站,有没有可能让最小的最大距离小于等于 D


假设答案为 D*, 对于 d < D*,possibe(d) = False,对于d > D*,possible(d) = True。针对单调问题的二分搜索是一个很典型的做法,重点是 possible(D) 的实现。


class Solution: def minmaxGasDist(self, stations: List[int], K: int) -> float:
def count(d): res = 0 for i in range(1, len(stations)): res += int((stations[i] - stations[i-1]) / d) return res
left = 0 right = 10**8
while right-left > 0.000001: mid = left + (right-left)/2 if count(mid) > K: left = mid else: right = mid return right

彩蛋:盆盆是🐷