2187. Minimum Time to Complete Trips
Problem Description
You have an array time where time[i] represents how long it takes the i-th bus to complete one trip.
Each bus can make multiple trips one after another - as soon as a bus finishes one trip, it can immediately start the next trip. All buses operate independently, meaning they don't affect each other's schedules.
Given an integer totalTrips, which represents the total number of trips that need to be completed by all buses combined, you need to find the minimum amount of time required for all buses together to complete at least totalTrips trips.
For example, if you have buses with trip times [1, 2, 3] and need totalTrips = 5 trips total:
- In 3 units of time: Bus 1 completes 3 trips, Bus 2 completes 1 trip, Bus 3 completes 1 trip = 5 trips total
- So the minimum time would be 3
The task is to determine the shortest time period in which the combined effort of all buses can achieve the required number of trips.
Intuition
The key insight is recognizing that this problem has a monotonic property: if we can complete at least totalTrips in time t, then we can definitely complete at least totalTrips in any time greater than t. This monotonic nature immediately suggests binary search as a potential approach.
Think about it this way - given a specific amount of time t, we can calculate exactly how many trips each bus can complete: t // time[i] for bus i. The total number of trips completed would be the sum of all individual bus trips: sum(t // time[i] for all i).
Now we need to find the minimum time where this sum is at least totalTrips. We could check every possible time value starting from 1, but that would be inefficient. Instead, we can use binary search to quickly narrow down the answer.
For the search boundaries:
- The minimum possible time is 1 (we need at least 1 unit of time to complete any trip)
- The maximum time we'd ever need is when only the fastest bus is working to complete all trips:
min(time) * totalTrips
Within this range, we binary search for the smallest time value where the total number of completed trips meets or exceeds totalTrips. The beauty of this approach is that we're essentially converting the problem from "find the minimum time" to "check if a given time is sufficient", which is much easier to compute.
The solution cleverly uses bisect_left with a key function that calculates the number of trips possible at each time point, searching for when we first reach totalTrips completed trips.
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def minimumTime(self, time: List[int], totalTrips: int) -> int:
3 # Define feasible function: can we complete totalTrips in given time?
4 def feasible(current_time: int) -> bool:
5 total_trips = sum(current_time // bus_time for bus_time in time)
6 return total_trips >= totalTrips
7
8 # Binary search setup
9 left = 1 # Minimum possible time
10 right = min(time) * totalTrips # Maximum time if only fastest bus works
11 first_true_index = -1
12
13 # Binary search for the first feasible time
14 while left <= right:
15 mid = (left + right) // 2
16 if feasible(mid):
17 first_true_index = mid # Found a valid answer
18 right = mid - 1 # Try to find smaller time
19 else:
20 left = mid + 1 # Need more time
21
22 return first_true_index
231class Solution {
2 public long minimumTime(int[] time, int totalTrips) {
3 // Find the minimum bus time for upper bound calculation
4 int minBusTime = time[0];
5 for (int busTime : time) {
6 minBusTime = Math.min(minBusTime, busTime);
7 }
8
9 // Binary search setup
10 long left = 1; // Minimum possible time
11 long right = (long) minBusTime * totalTrips; // Maximum time needed
12 long firstTrueIndex = -1;
13
14 // Binary search for the first feasible time
15 while (left <= right) {
16 long mid = left + (right - left) / 2;
17
18 // Check if we can complete totalTrips in mid time
19 if (feasible(time, mid, totalTrips)) {
20 firstTrueIndex = mid; // Found a valid answer
21 right = mid - 1; // Try to find smaller time
22 } else {
23 left = mid + 1; // Need more time
24 }
25 }
26
27 return firstTrueIndex;
28 }
29
30 // Feasible function: can we complete totalTrips in given time?
31 private boolean feasible(int[] time, long currentTime, int totalTrips) {
32 long tripsCompleted = 0;
33 for (int busTime : time) {
34 tripsCompleted += currentTime / busTime;
35 }
36 return tripsCompleted >= totalTrips;
37 }
38}
391class Solution {
2public:
3 long long minimumTime(vector<int>& time, int totalTrips) {
4 // Find the minimum bus time for upper bound calculation
5 int minBusTime = *min_element(time.begin(), time.end());
6
7 // Binary search setup
8 long long left = 1; // Minimum possible time
9 long long right = static_cast<long long>(minBusTime) * totalTrips;
10 long long firstTrueIndex = -1;
11
12 // Feasible function: can we complete totalTrips in given time?
13 auto feasible = [&](long long currentTime) -> bool {
14 long long tripsCompleted = 0;
15 for (int busTime : time) {
16 tripsCompleted += currentTime / busTime;
17 }
18 return tripsCompleted >= totalTrips;
19 };
20
21 // Binary search for the first feasible time
22 while (left <= right) {
23 long long mid = left + (right - left) / 2;
24
25 if (feasible(mid)) {
26 firstTrueIndex = mid; // Found a valid answer
27 right = mid - 1; // Try to find smaller time
28 } else {
29 left = mid + 1; // Need more time
30 }
31 }
32
33 return firstTrueIndex;
34 }
35};
361function minimumTime(time: number[], totalTrips: number): number {
2 // Feasible function: can we complete totalTrips in given time?
3 const feasible = (currentTime: bigint): boolean => {
4 let tripsCompleted: bigint = 0n;
5 for (const busTime of time) {
6 tripsCompleted += currentTime / BigInt(busTime);
7 }
8 return tripsCompleted >= BigInt(totalTrips);
9 };
10
11 // Binary search setup
12 let left: bigint = 1n; // Minimum possible time
13 let right: bigint = BigInt(Math.min(...time)) * BigInt(totalTrips);
14 let firstTrueIndex: bigint = -1n;
15
16 // Binary search for the first feasible time
17 while (left <= right) {
18 const mid: bigint = (left + right) / 2n;
19
20 if (feasible(mid)) {
21 firstTrueIndex = mid; // Found a valid answer
22 right = mid - 1n; // Try to find smaller time
23 } else {
24 left = mid + 1n; // Need more time
25 }
26 }
27
28 return Number(firstTrueIndex);
29}
30Solution Approach
The solution uses the binary search template to find the minimum time required. We're looking for the first time value where we can complete at least totalTrips - a classic first_true_index pattern.
Defining the Feasible Function:
A time value t is feasible if we can complete at least totalTrips in time t:
feasible(t) = sum(t // busTime for busTime in time) >= totalTrips
This creates the pattern: false, false, ..., true, true, true
We want to find the first true (minimum feasible time).
Setting Up the Search Range:
left = 1: Minimum possible time (at least 1 unit needed for any trip)right = min(time) * totalTrips: Maximum time needed if only the fastest bus operates
Binary Search Template:
left, right = 1, min(time) * totalTrips
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if feasible(mid): # Can complete totalTrips in mid time
first_true_index = mid
right = mid - 1 # Try to find smaller time
else:
left = mid + 1 # Need more time
return first_true_index
Why This Works:
- When
feasible(mid)is true, we've found a valid answer but want to minimize, so we search left (right = mid - 1) - When
feasible(mid)is false, we need more time, so we search right (left = mid + 1) first_true_indexalways holds the smallest feasible time found so far
Time Complexity: O(n × log(min(time) × totalTrips)) where n is the number of buses.
Space Complexity: O(1) - only constant extra space used.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through with time = [2, 3, 5] and totalTrips = 8.
Initialize:
left = 1,right = min(2, 3, 5) × 8 = 16first_true_index = -1
Iteration 1: left = 1, right = 16
mid = (1 + 16) // 2 = 8- Trips at time 8:
8//2 + 8//3 + 8//5 = 4 + 2 + 1 = 7 feasible(8) = (7 >= 8)→ falseleft = 8 + 1 = 9
Iteration 2: left = 9, right = 16
mid = (9 + 16) // 2 = 12- Trips at time 12:
12//2 + 12//3 + 12//5 = 6 + 4 + 2 = 12 feasible(12) = (12 >= 8)→ truefirst_true_index = 12,right = 12 - 1 = 11
Iteration 3: left = 9, right = 11
mid = (9 + 11) // 2 = 10- Trips at time 10:
10//2 + 10//3 + 10//5 = 5 + 3 + 2 = 10 feasible(10) = (10 >= 8)→ truefirst_true_index = 10,right = 10 - 1 = 9
Iteration 4: left = 9, right = 9
mid = (9 + 9) // 2 = 9- Trips at time 9:
9//2 + 9//3 + 9//5 = 4 + 3 + 1 = 8 feasible(9) = (8 >= 8)→ truefirst_true_index = 9,right = 9 - 1 = 8
Termination: left = 9 > right = 8, exit loop
Result: first_true_index = 9
Verification: At time 9, buses complete exactly 8 trips. At time 8, only 7 trips - insufficient.
Time and Space Complexity
The time complexity is O(n × log(m × k)), where:
nis the length of the arraytime(used in the sum computation within the key function)mis the minimum value in the arraytime(found bymin(time))kistotalTrips
The binary search operates on a range from 0 to mx = min(time) × totalTrips, which has O(m × k) elements. Each binary search iteration calls the key function lambda x: sum(x // v for v in time), which takes O(n) time to compute the sum over all buses. Since binary search performs O(log(m × k)) iterations, the total time complexity is O(n × log(m × k)).
The space complexity is O(1) as the algorithm only uses a constant amount of extra space for variables. The bisect_left function with a range object and key function doesn't create the entire range in memory but rather computes values on-the-fly, maintaining constant space usage.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common mistake is using the while left < right with right = mid variant instead of the standard template with first_true_index tracking.
Wrong approach:
while left < right: mid = (left + right) // 2 if feasible(mid): right = mid # Wrong: should be right = mid - 1 else: left = mid + 1 return left # Wrong: should return first_true_index
Correct approach (using template):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index
2. Integer Overflow for Large Inputs
When totalTrips is very large (up to 10^7) and min(time) is also large, the multiplication min(time) * totalTrips could overflow in languages with fixed integer sizes.
Solution: Use appropriate data types (long in Java, long long in C++, bigint in TypeScript):
long right = (long) minTime * totalTrips; // Cast to long before multiplication
3. Forgetting to Handle Edge Cases
When all buses are very slow or totalTrips is 1, the result might be at the boundary.
Solution: The template handles this naturally since first_true_index will be set on the first feasible value found.
4. Off-by-One in Search Range
Starting with left = 0 instead of left = 1 can cause issues since time 0 results in 0 trips.
Solution: Always start with left = 1 as the minimum meaningful time value.
What's the output of running the following function using the following tree as input?

1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
121import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
181function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!