二分算法02

二分算法02

  • 1. 每个小孩最多能分到多少糖果
  • 2. 准时到达的列车最小时速
  • 3. 在 D 天内送达包裹的能力

1. 每个小孩最多能分到多少糖果

给你一个 下标从 0 开始 的整数数组 candies 。数组中的每个元素表示大小为 candies[i] 的一堆糖果。你可以将每堆糖果分成任意数量的 子堆 ,但 无法 再将两堆合并到一起。

另给你一个整数 k 。你需要将这些糖果分配给 k 个小孩,使每个小孩分到 相同 数量的糖果。每个小孩可以拿走 至多一堆 糖果,有些糖果可能会不被分配。

返回每个小孩可以拿走的 最大糖果数目 。

真题点击此处:2226. 每个小孩最多能分到多少糖果

解题思路:
假设有一个长度为 n 的数组 candies,表示每个糖果的数量。现在我们希望将这些糖果平均分配给 k 个孩子。我们需要找到一个最大的正整数 x,使得将每个糖果分成 x 份后,每个孩子都可以得到至少一份,并且可以得到最大总糖果数。

  1. 我们首先定义两个指针 left 和 right,分别指向可能的最小值 1 和最大值 max(candies)。
  2. 然后我们使用二分查找的方法,不断缩小 left 和 right 的范围,直到 left > right 为止。
  3. 在每次循环中,我们计算 mid = (left + right) // 2,表示当前的糖果份数。
  4. 接着,我们遍历数组 candies,计算出将每个糖果分成 mid 份后,可以得到的总糖果数 ans。
  5. 如果 ans < k,说明当前的份数过多,我们需要将 right 指针左移,将搜索范围从右半部分继续缩小。
  6. 如果 ans >= k,说明当前份数过少或刚好符合要求,我们需要将 left 指针右移,将搜索范围从左半部分继续缩小。
  7. 最终返回 left - 1,即为最大的正整数 x。

具体解题思路可以总结为以下几点:

  • 定义两个指针 left 和 right,分别指向可能的最小值 1 和最大值 max(candies)。
  • 使用二分查找的方法,不断缩小 left 和 right 的范围,直到 left > right 为止。
  • 在每次循环中,计算 mid = (left + right) // 2,表示当前的糖果份数。
  • 遍历数组 candies,计算出将每个糖果分成 mid 份后,可以得到的总糖果数 ans。
  • 如果 ans < k,将 right 指针左移,将搜索范围从右半部分继续缩小。
  • 如果 ans >= k,将 left 指针右移,将搜索范围从左半部分继续缩小。
  • 最终返回 left - 1,即为最大的正整数 x。

这种方法利用了二分查找的特性,不断缩小搜索范围,最终得到符合要求的最大的正整数 x。

以下为代码实现:

class Solution:
    def maximumCandies(self, candies: List[int], k: int) -> int:
        left, right = 1, max(candies)

        while left <= right:
            mid = (left + right) // 2
            ans = 0
            for c in candies:
                ans += c // mid
            if ans < k:
                right = mid - 1
            else:
                left = mid + 1

        return left - 1

时间复杂度:O(nlogm),其中 n 为糖果数组 candies 的长度,m 为糖果数组中最大的数值。

空间复杂度:O(1),该算法只使用了常数级别的额外空间用于存储变量。

2. 准时到达的列车最小时速

给你一个浮点数 hour ,表示你到达办公室可用的总通勤时间。要到达办公室,你必须按给定次序乘坐 n 趟列车。另给你一个长度为 n 的整数数组 dist ,其中 dist[i] 表示第 i 趟列车的行驶距离(单位是千米)。

每趟列车均只能在整点发车,所以你可能需要在两趟列车之间等待一段时间。

例如,第 1 趟列车需要 1.5 小时,那你必须再等待 0.5 小时,搭乘在第 2 小时发车的第 2 趟列车。
返回能满足你准时到达办公室所要求全部列车的 最小正整数 时速(单位:千米每小时),如果无法准时到达,则返回 -1 。

生成的测试用例保证答案不超过 107 ,且 hour 的 小数点后最多存在两位数字 。

真题点击此处:1870. 准时到达的列车最小时速

解题思路:
假设有一个长度为 n 的数组 dist,表示一系列区间的距离,以及一个代表旅行所需总时间的浮点数 hour。现在我们需要找到一个最小的速度 x,使得以这个速度行驶的话,可以在规定时间内完成旅程。

  1. 首先定义两个指针 left 和 right,分别指向可能的最小速度 1 和最大速度 10^7。
  2. 使用二分查找的方法,不断缩小 left 和 right 的范围,直到 left > right 为止。
  3. 在每次循环中,计算 mid = (left + right) // 2,表示当前的速度。
  4. 遍历数组 dist,计算以速度 mid 行驶时,所需的总时间 time。
  5. 如果 time <= hour,说明当前速度可以满足要求,我们需要将 right 指针左移,将搜索范围从右半部分继续缩小。
  6. 如果 time > hour,说明当前速度过慢,我们需要将 left 指针右移,将搜索范围从左半部分继续缩小。
  7. 最终返回 left,即为最小的速度 x。

具体解题思路可以总结为以下几点:

  • 定义两个指针 left 和 right,分别指向可能的最小速度 1 和最大速度 10^7。
  • 使用二分查找的方法,不断缩小 left 和 right 的范围,直到 left > right 为止。
  • 在每次循环中,计算 mid = (left + right) // 2,表示当前的速度。
  • 遍历数组 dist,计算以速度 mid 行驶时,所需的总时间 time。
  • 如果 time <= hour,将 right 指针左移,将搜索范围从右半部分继续缩小。
  • 如果 time > hour,将 left 指针右移,将搜索范围从左半部分继续缩小。
  • 最终返回 left,即为最小的速度 x。

这种方法利用了二分查找的特性,不断缩小搜索范围,最终得到符合要求的最小速度 x。

以下为代码实现:

class Solution:
    def minSpeedOnTime(self, dist: List[int], hour: float) -> int:
        left, right = 1, 10 ** 7
        while left <= right:
            mid = (left + right) // 2
            time = 0
            for i in range(len(dist) - 1):
                time += (dist[i] + mid - 1) // mid
            time += dist[-1] / mid
            if time <= hour:
                right = mid - 1
            else:
                left = mid + 1
        return left if len(dist) < hour + 1 else -1

时间复杂度: O(nlogm),其中 n 为距离数组 dist 的长度,m 为距离数组中的最大值。

空间复杂度:O(1),该算法只使用了常数级别的额外空间用于存储变量。

3. 在 D 天内送达包裹的能力

传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。

传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。

返回能在 days 天内将传送带上的所有包裹送达的船的最低运载能力。

真题点击此处:1011. 在 D 天内送达包裹的能力

解题思路:

  • 定义两个指针 left 和 right,分别指向货物重量的最大值和总和。
  • 使用二分查找的方法,不断缩小 left 和 right 的范围,直到 left > right 为止。
  • 在每次循环中,计算 mid = (left + right) // 2,表示当前的运载能力。
  • 遍历货物重量数组 weights,模拟运输过程,统计完成的运输批次 ans。
  • 根据 ans 与规定天数 days 的关系,调整 left 和 right 指针的位置。
  • 最终返回 left,即为最低的运载能力 x。

以下为代码实现:

class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -> int:
        left, right = max(weights), sum(weights)

        while left <= right:
            mid = (left + right) // 2
            ans = 0
            s = mid
            i = 0
            while i < len(weights):
                if s >= weights[i]:
                    s -= weights[i]
                    i += 1
                else:
                    ans += 1
                    s = mid
            ans += 1
            if ans <= days:
                right = mid - 1
            else:
                left = mid + 1
        return left

时间复杂度:O(nlogm),其中 n 为重量数组 weights 的长度,m 为所有重量的总和。

空间复杂度:O(1),该算法只使用了常数级别的额外空间用于存储变量。

你可能感兴趣的:(算法学习,算法)