题目描述
给你一个数组 time ,其中 time[i] 表示第 i 辆公交车完成 一趟 旅途 所需要花费的时间。
每辆公交车可以 连续 完成多趟旅途,也就是说,一辆公交车当前旅途完成后,可以 立马开始 下一趟旅途。每辆公交车 独立 运行,也就是说可以同时有多辆公交车在运行且互不影响。
给你一个整数 totalTrips ,表示所有公交车 总共 需要完成的旅途数目。请你返回完成 至少 totalTrips 趟旅途需要花费的 最少 时间。
示例 1:
输入:time = [1,2,3], totalTrips = 5
输出:3
解释:
- 时刻 t = 1 ,每辆公交车完成的旅途数分别为 [1,0,0] 。
已完成的总旅途数为 1 + 0 + 0 = 1 。
- 时刻 t = 2 ,每辆公交车完成的旅途数分别为 [2,1,0] 。
已完成的总旅途数为 2 + 1 + 0 = 3 。
- 时刻 t = 3 ,每辆公交车完成的旅途数分别为 [3,1,1] 。
已完成的总旅途数为 3 + 1 + 1 = 5 。
所以总共完成至少 5 趟旅途的最少时间为 3 。
示例 2:
输入:time = [2], totalTrips = 1
输出:2
解释:
只有一辆公交车,它将在时刻 t = 2 完成第一趟旅途。
所以完成 1 趟旅途的最少时间为 2 。
提示:
1 <= time.length <= 1051 <= time[i], totalTrips <= 107
解决方案:
算法目标
给定多辆车的单趟完成时间和需要完成的总趟数,找出完成所有趟数所需的最少时间。
核心思路
-
确定时间范围 :时间在
[0, 最慢车×总趟数]之间 -
二分查找时间:尝试不同的时间,计算在该时间内能完成多少趟
-
寻找最小值:找到能满足总趟数要求的最小时间
算法步骤
1. 确定查找范围
int min_tmp = *min_element(time.begin(), time.end()); long long left = -1; // 不可行的下界 long long right = (long long)min_tmp * totalTrips; // 可行的上界
-
下界:-1(肯定不够的时间)
-
上界:用最慢的车完成所有趟数的时间
-
使用开区间
(left, right),left永远不可行,right永远可行
2. 二分查找主循环
while(left + 1 < right) { long long mid = left + (right - left) / 2; // 尝试的时间 // 计算在时间mid内能完成的趟数 // 判断并更新边界 }
3. 计算可完成趟数
long long tmp = 0; for(auto p : time) { tmp += mid / p; // 每辆车在时间mid内能完成的趟数 if(tmp >= totalTrips) break; // 提前退出优化 }
-
对每辆车:在时间
mid内能完成mid / time[i]趟 -
累加所有车的趟数
-
提前退出:当已满足总趟数要求时停止计算
4. 判断并更新边界
if(tmp < totalTrips) {
left = mid; // 时间不够,需要更多时间
} else {
right = mid; // 时间足够,尝试更少时间
}
5. 返回结果
return right; // 最小的可行时间
关键点
-
二分查找对象 :总时间
t,不是车辆数 -
验证条件 :在时间
t内能完成的趟数≥ totalTrips -
搜索方向 :寻找满足条件的最小
t -
整数除法 :
mid / p自动向下取整,因为一趟必须完整完成
时间复杂度
-
寻找最慢车:O(n)
-
二分查找:O(log(min_time × totalTrips))
-
每次验证:O(n)
-
总时间:O(n log T)
示例
time = [1, 2, 3]
totalTrips = 5
查找过程:
1. 范围: t ∈ [0, 1×5=5]
2. 尝试 t=2: 可完成3趟 < 5 → 不够
3. 尝试 t=3: 可完成5趟 ≥ 5 → 足够
4. 最终结果: t=3
算法正确性
-
单调性:时间越长,能完成的趟数越多
-
边界保证:
left永远不可行,right永远可行 -
最终
right是最小的可行时间
优化亮点
-
提前退出:当趟数已达标时立即停止计算
-
开区间二分:避免边界处理复杂
-
类型安全 :使用
long long防止溢出
函数源码:
cppclass Solution { public: long long minimumTime(vector<int>& time, int totalTrips) { int min_tmp=time[0]; for(auto p:time){ min_tmp=min(p,min_tmp); } long long left=-1; long long right=(long long)min_tmp*totalTrips; while(left+1<right){ long long mid=(left+right)/2; long long tmp=0; for(auto p:time){ tmp+=mid/p; if(tmp >= totalTrips) break; // 提前退出优化 } if(tmp<totalTrips) left=mid; else right=mid; } return right; } };