题目描述:
给你一个浮点数 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
中,小数点后最多存在两位数字
读懂题目
假设你需要通过乘坐几趟列车到达办公室。每趟列车有固定的行驶距离,而你拥有一个限定的总时间来完成这段旅程。为了简化问题,我们假设每趟列车只能在整点时刻发车,如果列车在非整点到达车站,你需要等到下一个整点才能继续乘坐下一趟列车。
理解输入条件
-
整数数组
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公里。
-
浮点数
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.5 1.5 1.5 小时,你需要等待到第 2 2 2 个小时才能继续下一趟列车。
-
-
最后一趟列车不需要整点发车
-
问题:只有最后一趟列车不需要等到整点发车,可以直接到达目的地。
-
陷阱:错误地对最后一趟列车也进行整点发车计算,导致时间计算错误。
-
-
边界值处理
-
问题:时速和时间的边界值处理非常重要。时速不能为 0 0 0,且题目保证时速不超过 1 0 7 10^7 107。
-
陷阱:初始化时速的边界值时,可能会设定错误的初始值,比如
left
为 0 0 0 或 − 1 -1 −1,而实际上时速最小应该为 1 1 1。
-
-
浮点数精度问题
-
问题:输入的总时间
hour
是浮点数,需要精确处理,否则可能会出现精度误差。 -
陷阱:直接使用浮点数进行比较时,可能会出现精度问题,导致判断错误。应注意使用浮点数的比较方法。
-
-
时间复杂度
-
问题:在计算每个速度是否能在给定时间内到达时,需要注意算法的时间复杂度。
-
陷阱:如果直接使用线性查找,会导致时间复杂度过高。使用二分查找可以有效降低时间复杂度。
-
实现一:二分查找(新手版)
- 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
解释:优化版代码把之前计算的时间乘回速度,变成距离,然后直接加上最后一段的距离。这样做避免了除法运算,而是在最后统一比较。减少除法运算可以提高计算效率,特别是在处理大量数据时。