LeetCode 2187 - Minimum Time to Complete Trips
The problem asks us to determine the minimum time required for a fleet of buses to collectively complete a given number of trips. Each bus in the fleet has its own fixed trip duration, denoted by the array time.
Difficulty: 🟡 Medium
Topics: Array, Binary Search
Solution
Problem Understanding
The problem asks us to determine the minimum time required for a fleet of buses to collectively complete a given number of trips. Each bus in the fleet has its own fixed trip duration, denoted by the array time. The buses operate independently and continuously, meaning that a bus can start its next trip immediately after finishing its current trip.
The input consists of an array time of integers, where time[i] represents the time required for bus i to finish one trip, and an integer totalTrips, which represents the total number of trips that all buses must complete. The output is a single integer representing the smallest amount of time required so that the sum of trips completed by all buses is at least totalTrips.
The constraints indicate that time.length can be up to $10^5$ and individual trip times and total trips can be up to $10^7$. This suggests that a naive simulation of every trip is infeasible due to time complexity. Instead, we need an efficient algorithm that avoids iterating over every possible trip individually.
Important edge cases to consider include:
- Only one bus is available.
totalTripsis very small (e.g., 1).- Multiple buses have the same trip times.
- Very large
totalTripswith a mix of fast and slow buses.
These edge cases challenge naive implementations and integer handling.
Approaches
A brute-force approach would simulate time incrementally, at each unit of time counting how many trips each bus has completed until the total trips reach totalTrips. While this guarantees correctness, it is too slow. If the fastest bus has a time of 1 and totalTrips is $10^7$, this approach would iterate $10^7$ times, which is not acceptable.
The key observation for a better solution is that the total number of trips completed by a bus at a given time t is simply t // time[i]. This allows us to calculate the total trips across all buses without simulation. Since the total trips is a non-decreasing function of time, we can apply binary search to find the minimum time t such that the sum of trips is at least totalTrips.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(totalTrips * n) | O(1) | Incrementally simulate each time unit and count trips |
| Optimal (Binary Search) | O(n log(maxTime * totalTrips)) | O(1) | Use the non-decreasing property of trips over time to binary search for minimum time |
Algorithm Walkthrough
- Identify the search space for binary search. The minimum possible time is 1 (or the minimum trip time), and the maximum possible time is
min(time) * totalTripsbecause if the fastest bus does all trips, it would take this long. - Initialize
leftas 1 andrightasmin(time) * totalTrips. - While
left < right, compute the middle pointmid = left + (right - left) // 2. - Calculate the total trips completed at time
midas the sum ofmid // time[i]for each busi. - If the total trips at
midis greater than or equal tototalTrips, updateright = mid. Otherwise, setleft = mid + 1. - When the loop terminates,
left(orright) will hold the minimum time required to complete at leasttotalTrips.
Why it works: The total number of trips is a non-decreasing function of time. By performing a binary search over possible times, we efficiently find the smallest time that satisfies the condition. The invariant maintained is that left always points to a time that might satisfy the condition, and right always points to a time that definitely satisfies it.
Python Solution
from typing import List
class Solution:
def minimumTime(self, time: List[int], totalTrips: int) -> int:
left, right = 1, min(time) * totalTrips
while left < right:
mid = left + (right - left) // 2
trips = sum(mid // t for t in time)
if trips >= totalTrips:
right = mid
else:
left = mid + 1
return left
The Python code initializes the search range using the fastest bus multiplied by totalTrips. In each iteration, it computes the number of trips achievable at mid and narrows the search space. The loop terminates when left equals the minimum feasible time.
Go Solution
func minimumTime(time []int, totalTrips int) int64 {
minTime := time[0]
for _, t := range time {
if t < minTime {
minTime = t
}
}
left, right := int64(1), int64(minTime)*int64(totalTrips)
for left < right {
mid := left + (right-left)/2
var trips int64 = 0
for _, t := range time {
trips += mid / int64(t)
}
if trips >= int64(totalTrips) {
right = mid
} else {
left = mid + 1
}
}
return left
}
The Go implementation differs slightly due to type handling. We use int64 to prevent overflow when multiplying minTime by totalTrips. The binary search logic is identical.
Worked Examples
Example 1:
time = [1,2,3], totalTrips = 5
| Iteration | left | right | mid | trips calculation | total trips | action |
|---|---|---|---|---|---|---|
| 1 | 1 | 5 | 3 | 3//1 + 3//2 + 3//3 = 3+1+1 | 5 | trips >= totalTrips → right = mid |
| 2 | 1 | 3 | 2 | 2//1 + 2//2 + 2//3 = 2+1+0 | 3 | trips < totalTrips → left = mid+1 |
| 3 | 3 | 3 | - | - | - | loop ends, answer = 3 |
Example 2:
time = [2], totalTrips = 1
| Iteration | left | right | mid | trips calculation | total trips | action |
|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 1 | 1//2 = 0 | 0 | trips < totalTrips → left = 2 |
| 2 | 2 | 2 | - | - | - | loop ends, answer = 2 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log(maxTime * totalTrips)) | Binary search over time range; summing trips over n buses per iteration |
| Space | O(1) | Only variables for search bounds and temporary sum are used |
The logarithmic factor comes from the search over the time range, which can go up to min(time) * totalTrips. Each check sums over n buses.
Test Cases
# Provided examples
assert Solution().minimumTime([1,2,3], 5) == 3 # Example 1
assert Solution().minimumTime([2], 1) == 2 # Example 2
# Edge and boundary cases
assert Solution().minimumTime([1], 1) == 1 # Single bus, single trip
assert Solution().minimumTime([5,10,2], 10) == 8 # Mixed bus times
assert Solution().minimumTime([10000000], 10000000) == 100000000000000 # Large values
assert Solution().minimumTime([1,1,1], 10000000) == 3333334 # Many buses, small times
assert Solution().minimumTime([2,3,7], 1) == 2 # Only need one trip
| Test | Why |
|---|---|
[1,2,3], 5 |
Validates multiple buses with different times |
[2], 1 |
Single bus scenario |
[1],1 |
Minimal input size |
[5,10,2], 10 |
Mixed bus speeds for larger trips |
[10000000],10000000 |
Checks handling of very large numbers |
[1,1,1],10000000 |
Tests many fast buses and large totalTrips |
[2,3,7],1 |
Ensures correct answer when only a single trip is needed |
Edge Cases
One important edge case is a single bus with a large number of trips. This tests whether the algorithm can handle high values without integer overflow and correctly computes multiples of one bus's trip time.
A second edge case is multiple buses with the same trip time, which could lead to off-by-one errors if the binary search bounds are not handled carefully. The algorithm correctly sums mid // time[i] for each bus, ensuring accurate calculation.
A third edge case is very small totalTrips relative to large bus times, e.g., totalTrips = 1 and `time = [