二分查找 | 时速最小的值

题目: 1870. 准时到达的列车最小时速

题目描述:

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

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

  • 例如,第 1 趟列车需要 1.5 小时,那你必须再等待 0.5 小时,搭乘在第 2 小时发车的第 2 趟列车。

返回能满足你准时到达办公室所要求全部列车的 最小正整数 时速(单位:千米每小时),如果无法准时到达,则返回 -1

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

示例 1:

输入:dist = [1,3,2], hour = 6
输出:1
解释:速度为 1 时:
- 第 1 趟列车运行需要 1/1 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 1 小时发车的列车。第 2 趟列车运行需要 3/1 = 3 小时。
- 由于是在整数时间到达,可以立即换乘在第 4 小时发车的列车。第 3 趟列车运行需要 2/1 = 2 小时。
- 你将会恰好在第 6 小时到达。

示例 2:

输入:dist = [1,3,2], hour = 2.7
输出:3
解释:速度为 3 时:
- 第 1 趟列车运行需要 1/3 = 0.33333 小时。
- 由于不是在整数时间到达,故需要等待至第 1 小时才能搭乘列车。第 2 趟列车运行需要 3/3 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 2 小时发车的列车。第 3 趟列车运行需要 2/3 = 0.66667 小时。
- 你将会在第 2.66667 小时到达。

示例 3:

输入:dist = [1,3,2], hour = 1.9
输出:-1
解释:不可能准时到达,因为第 3 趟列车最早是在第 2 小时发车。

提示:

  • n == dist.length
  • 1 <= n <= 105
  • 1 <= dist[i] <= 105
  • 1 <= hour <= 109
  • hours 中,小数点后最多存在两位数字

读懂题目

假设你需要通过乘坐几趟列车到达办公室。每趟列车有固定的行驶距离,而你拥有一个限定的总时间来完成这段旅程。为了简化问题,我们假设每趟列车只能在整点时刻发车,如果列车在非整点到达车站,你需要等到下一个整点才能继续乘坐下一趟列车。

理解输入条件

  1. 整数数组 dist

    • 这个数组包含了每趟列车的行驶距离。比如 d i s t = [ 5 , 8 , 3 ] dist = [5, 8, 3] dist=[5,8,3],意味着第一趟列车行驶 5 5 5公里**,第二趟行驶 8 8 8公里,第三趟行驶 3 3 3公里。
  2. 浮点数 hour

    • 这个数字表示你有多少小时可以用来完成旅程。例如, h o u r = 2.7 hour = 2.7 hour=2.7表示你有 2 2 2小时零 42 42 42分钟(因为 0.7 0.7 0.7小时等于 42 42 42分钟)的时间到达办公室。

理解输出条件

最小正整数时速

  • 你需要计算出一个最小的时速(公里/小时),使得你在给定的总时间内准时到达办公室。如果不存在这样的时速,则输出$ -1$。

理解约束条件

时速范围

  • 题目中提到,答案的时速不会超过 1 0 7 10^7 107 公里/小时,这是一个速度的上限。

背后的陷阱点

  1. 整点发车

    • 问题:每趟列车只能在整点发车,这意味着即使当前列车行驶时间不到一个小时,也要等到下一个整点才能发车。

    • 陷阱:忘记考虑整点发车的情况。比如,若一趟列车行驶时间是 1.5 1.5 1.5 小时,你需要等待到第 2 2 2 个小时才能继续下一趟列车。

  2. 最后一趟列车不需要整点发车

    • 问题:只有最后一趟列车不需要等到整点发车,可以直接到达目的地。

    • 陷阱:错误地对最后一趟列车也进行整点发车计算,导致时间计算错误。

  3. 边界值处理

    • 问题:时速和时间的边界值处理非常重要。时速不能为 0 0 0,且题目保证时速不超过 1 0 7 10^7 107

    • 陷阱:初始化时速的边界值时,可能会设定错误的初始值,比如 left 0 0 0 − 1 -1 1,而实际上时速最小应该为 1 1 1

  4. 浮点数精度问题

    • 问题:输入的总时间 hour 是浮点数,需要精确处理,否则可能会出现精度误差。

    • 陷阱:直接使用浮点数进行比较时,可能会出现精度问题,导致判断错误。应注意使用浮点数的比较方法。

  5. 时间复杂度

    • 问题:在计算每个速度是否能在给定时间内到达时,需要注意算法的时间复杂度。

    • 陷阱:如果直接使用线性查找,会导致时间复杂度过高。使用二分查找可以有效降低时间复杂度。

实现一:二分查找(新手版)

  • can_arrive_on_time: 这个函数检查在给定的速度下是否可以在规定时间内到达目的地,考虑到除最后一趟列车外,每趟列车需要整点发车的约束。
  • lower_bound: 这个函数使用二分查找方法来找到可能的最小时速,以确保在限定的时间内到达办公室。
  • minSpeedOnTime: 这是一个对外的接口函数,调用 lower_bound 来返回所需的最小时速。
from typing import List
import math

class Solution:
    def can_arrive_on_time(self, dist: List[int], hour: float, speed: int) -> bool:
        """
        判断给定速度下是否能在限定时间内完成所有列车的行程。

        参数:
        dist (List[int]): 每趟列车的行驶距离列表。
        hour (float): 总可用时间(小时)。
        speed (int): 假设的列车速度(公里/小时)。

        返回:
        bool: 如果能在限定时间内到达,则返回 True;否则返回 False。
        """
        total_time = 0.0  # 初始化总时间
        n = len(dist)

        # 计算除最后一趟列车外的每趟列车所需时间,并考虑整点发车规则
        for i in range(n - 1):  # 遍历前n-1趟列车,最后一趟列车不需要整点发车
            total_time += math.ceil(dist[i] / speed)

        # 计算最后一趟列车所需时间,不需要整点发车
        total_time += dist[-1] / speed  # 计算最后一趟列车所费时间
        return total_time <= hour  # 返回总时间是否小于或等于给定的总时间

    def lower_bound(self, dist: List[int], hour: float) -> int:
        """
        使用二分查找确定能够按时到达办公室的最小时速。

        参数:
        dist (List[int]): 每趟列车的行驶距离列表。
        hour (float): 总可用时间(小时)。

        返回:
        int: 最小的合适时速;如果无法按时到达,则返回 -1。
        """
        left, right = 1, 10**7  # 设置速度搜索范围,从1到10^7
        result = -1  # 若找不到合适的速度,默认返回 -1
        while left <= right:
            mid = (left + right) // 2
            if self.can_arrive_on_time(dist, hour, mid):
                result = mid  # 如果当前速度能够准时到达,则记录此速度
                right = mid - 1  # 继续缩小右边界,查找更小的合适速度
            else:
                left = mid + 1  # 否则,增加左边界,寻找更大的速度
        return result

    def minSpeedOnTime(self, dist: List[int], hour: float) -> int:
        """
        返回完成所有列车行程所需的最小时速。

        参数:
        dist (List[int]): 每趟列车的行驶距离列表。
        hour (float): 总可用时间(小时)。

        返回:
        int: 最小的合适时速;如果无法按时到达,则返回 -1。
        """
        return self.lower_bound(dist, hour)  # 调用二分查找函数来找到最小时速

实现二:二分查找(优化版)

class Solution:
    def calculate_travel_time(self, dist: List[int], speed: int) -> int:
        """计算给定速度下的总行程时间"""
        total_time = 0
        for i in range(len(dist) - 1):
            # 模拟向上取整的效果,但使用整数除法来实现
            total_time += (dist[i] - 1) // speed + 1  
        total_time *= speed
        total_time += dist[-1]
        return total_time

    def can_arrive_on_time(self, dist: List[int], hr: int, speed: int) -> bool:
        """检查给定速度是否能在规定时间内到达"""
        total_time = self.calculate_travel_time(dist, speed)
        return total_time * 100 <= hr * speed

    def binary_search_speed(self, dist: List[int], hr: int) -> int:
        """使用二分查找寻找最小满足条件的速度"""
        left, right = 1, 10**7
        while left < right:
            mid = left + (right - left) // 2
            if self.can_arrive_on_time(dist, hr, mid):
                right = mid
            else:
                left = mid + 1
        return left

    def minSpeedOnTime(self, dist: List[int], hour: float) -> int:
        """主函数:计算最小速度"""
        n = len(dist)
        hr = round(hour * 100)
        
        if hr <= 100 * (n - 1):
            return -1
        
        return self.binary_search_speed(dist, hr)

两份代码的比较

1. 浮点数处理

优化版代码:

hr = round(hour * 100)

新手代码:

# 直接使用 hour

解释:想象你在计算钱,但是计算机对小数不太擅长。优化版代码把小时变成"分"(乘100),然后四舍五入成整数。这就像把1.5元变成150分。用整数计算可以避免小数计算时可能出现的小错误,特别是在处理很多数字时。

2. 提前返回

优化版代码:

if hr <= 100 * (n - 1):
    return -1

新手代码:没有这个检查。

解释:假设你要坐3趟火车,每趟至少要1小时。如果总时间少于2小时,你肯定赶不上。优化版代码先检查这种情况,如果时间不够,立即返回-1,不再做后面的计算。

3. 时间计算方法

优化版代码:

t += (dist[i] - 1) // speed + 1  # 使用地板除法(向下取整) 

新手代码:

total_time += math.ceil(dist[i] / speed)  # 使用浮点数除法进行取整

解释:两种方法都是为了向上取整,但优化版代码避免了使用浮点数。想象你开车100公里,速度是60公里/小时。100/60=1.66小时,但实际上你需要2小时。优化版代码的算法就像是先减去1(变成99),除以60得到1,再加回1,最终得到2,实现了向上取整。

4. 最后一段的处理

优化版代码:

total_time *= speed
total_time += dist[-1]

新手代码:

total_time += dist[-1] / speed

解释:优化版代码把之前计算的时间乘回速度,变成距离,然后直接加上最后一段的距离。这样做避免了除法运算,而是在最后统一比较。减少除法运算可以提高计算效率,特别是在处理大量数据时。

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是希望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值