Facebook Pixel

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 Approach

The solution implements binary search to find the minimum time required. Let's walk through the implementation step by step:

1. Setting up the search range:

  • Left boundary: l = 1 (minimum possible time)
  • Right boundary: r = min(time) * totalTrips (maximum time needed if only the fastest bus operates)

The right boundary is calculated as min(time) * totalTrips because in the worst case, if only the fastest bus (with minimum trip time) were working alone, it would need this much time to complete all trips.

2. Binary search process:

While l < r:

  • Calculate the middle point: mid = (l + r) // 2
  • Count total trips possible in mid time: trips = sum(mid // t for t in time)
    • For each bus with trip time t, it can complete mid // t trips in mid time
    • Sum all these trips to get the total
  • If trips >= totalTrips:
    • We can complete the required trips in mid time
    • Try to find a smaller time: r = mid
  • Otherwise:
    • We need more time
    • Search in the upper half: l = mid + 1

3. Return the result:

When the loop ends, l will be the minimum time required.

Implementation using Python's bisect:

The provided solution uses Python's bisect_left function cleverly:

bisect_left(range(mx), totalTrips, key=lambda x: sum(x // v for v in time))

This searches in the range [0, mx) for the leftmost position where the key function returns a value >= totalTrips. The key function lambda x: sum(x // v for v in time) calculates how many trips can be completed in time x.

Time Complexity: O(n * log(min(time) * totalTrips)) where n is the length of the time array. The log factor comes from binary search, and we spend O(n) time in each iteration to calculate the total trips.

Space Complexity: O(1) as we only use a constant amount of extra space.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a concrete example:

  • time = [2, 3, 5] (bus trip times)
  • totalTrips = 8 (required trips)

Step 1: Initialize search boundaries

  • Left boundary: l = 1 (minimum possible time)
  • Right boundary: r = min(2, 3, 5) * 8 = 2 * 8 = 16

Step 2: Binary search iterations

Iteration 1:

  • mid = (1 + 16) // 2 = 8
  • Calculate trips at time 8:
    • Bus 1: 8 // 2 = 4 trips
    • Bus 2: 8 // 3 = 2 trips
    • Bus 3: 8 // 5 = 1 trip
    • Total: 4 + 2 + 1 = 7 trips
  • Since 7 < 8, we need more time
  • Update: l = 9, r = 16

Iteration 2:

  • mid = (9 + 16) // 2 = 12
  • Calculate trips at time 12:
    • Bus 1: 12 // 2 = 6 trips
    • Bus 2: 12 // 3 = 4 trips
    • Bus 3: 12 // 5 = 2 trips
    • Total: 6 + 4 + 2 = 12 trips
  • Since 12 >= 8, we might be able to do it faster
  • Update: l = 9, r = 12

Iteration 3:

  • mid = (9 + 12) // 2 = 10
  • Calculate trips at time 10:
    • Bus 1: 10 // 2 = 5 trips
    • Bus 2: 10 // 3 = 3 trips
    • Bus 3: 10 // 5 = 2 trips
    • Total: 5 + 3 + 2 = 10 trips
  • Since 10 >= 8, we might be able to do it faster
  • Update: l = 9, r = 10

Iteration 4:

  • mid = (9 + 10) // 2 = 9
  • Calculate trips at time 9:
    • Bus 1: 9 // 2 = 4 trips
    • Bus 2: 9 // 3 = 3 trips
    • Bus 3: 9 // 5 = 1 trip
    • Total: 4 + 3 + 1 = 8 trips
  • Since 8 >= 8, we've met our requirement!
  • Update: l = 9, r = 9

Step 3: Return result The loop terminates when l == r = 9. The minimum time needed is 9 units.

We can verify: At time 9, the buses complete exactly 8 trips total, meeting our requirement. At time 8, they only complete 7 trips, which is insufficient.

Solution Implementation

1class Solution:
2    def minimumTime(self, time: List[int], totalTrips: int) -> int:
3        # Calculate the maximum possible time needed
4        # Using minimum bus time * total trips as upper bound
5        max_time = min(time) * totalTrips
6      
7        # Binary search for the minimum time needed to complete totalTrips
8        # We search in range [0, max_time) for the first time value
9        # where we can complete at least totalTrips
10        return bisect_left(
11            range(max_time), 
12            totalTrips, 
13            key=lambda current_time: sum(current_time // bus_time for bus_time in time)
14        )
15```
16
17Wait, let me reconsider this solution. The code uses `bisect_left` but we need to import it. Also, let me think more carefully about the logic:
18
19```python3
20from bisect import bisect_left
21from typing import List
22
23class Solution:
24    def minimumTime(self, time: List[int], totalTrips: int) -> int:
25        # Calculate upper bound for binary search
26        # At minimum, we need min(time) * totalTrips time if only fastest bus works
27        max_possible_time = min(time) * totalTrips
28      
29        # Use binary search to find minimum time where we can complete totalTrips
30        # bisect_left finds the leftmost position where totalTrips can be inserted
31        # key function calculates how many trips can be completed in 'x' time
32        return bisect_left(
33            range(max_possible_time), 
34            totalTrips, 
35            key=lambda x: sum(x // bus_time for bus_time in time)
36        )
37
1class Solution {
2    /**
3     * Finds the minimum time required to complete totalTrips trips
4     * using buses with given time intervals.
5     * 
6     * @param time Array where time[i] represents the time needed for bus i to complete one trip
7     * @param totalTrips The total number of trips that need to be completed
8     * @return The minimum time required to complete all trips
9     */
10    public long minimumTime(int[] time, int totalTrips) {
11        // Find the minimum time among all buses
12        int minTime = time[0];
13        for (int busTime : time) {
14            minTime = Math.min(minTime, busTime);
15        }
16      
17        // Binary search boundaries
18        // Left: minimum possible time (at least 1 unit of time needed)
19        // Right: worst case - all trips done by fastest bus
20        long left = 1;
21        long right = (long) minTime * totalTrips;
22      
23        // Binary search to find minimum time
24        while (left < right) {
25            // Calculate middle point
26            long mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
27          
28            // Count total trips possible within 'mid' time
29            long totalTripsCompleted = 0;
30            for (int busTime : time) {
31                // Each bus can complete mid/busTime trips in 'mid' time
32                totalTripsCompleted += mid / busTime;
33            }
34          
35            // Check if we can complete required trips within 'mid' time
36            if (totalTripsCompleted >= totalTrips) {
37                // Can complete trips, try to minimize time further
38                right = mid;
39            } else {
40                // Cannot complete trips, need more time
41                left = mid + 1;
42            }
43        }
44      
45        return left;
46    }
47}
48
1class Solution {
2public:
3    long long minimumTime(vector<int>& time, int totalTrips) {
4        // Find the minimum time among all buses
5        int minTime = *min_element(time.begin(), time.end());
6      
7        // Binary search boundaries
8        // Left: minimum possible time is 1
9        // Right: maximum possible time is when the fastest bus completes all trips
10        long long left = 1;
11        long long right = static_cast<long long>(minTime) * totalTrips;
12      
13        // Binary search to find the minimum time needed
14        while (left < right) {
15            // Calculate middle point
16            long long mid = (left + right) / 2;
17          
18            // Count total trips that can be completed in 'mid' time
19            long long totalCompletedTrips = 0;
20            for (int busTime : time) {
21                totalCompletedTrips += mid / busTime;
22            }
23          
24            // Check if we can complete required trips in 'mid' time
25            if (totalCompletedTrips >= totalTrips) {
26                // Can complete all trips, try to find a smaller time
27                right = mid;
28            } else {
29                // Cannot complete all trips, need more time
30                left = mid + 1;
31            }
32        }
33      
34        // Return the minimum time needed to complete all trips
35        return left;
36    }
37};
38
1/**
2 * Finds the minimum time required for all buses to complete at least totalTrips trips
3 * Uses binary search to find the optimal time
4 * 
5 * @param time - Array where time[i] represents the time the i-th bus takes to complete one trip
6 * @param totalTrips - The total number of trips all buses should complete
7 * @returns The minimum time required for all buses to complete totalTrips
8 */
9function minimumTime(time: number[], totalTrips: number): number {
10    // Initialize binary search boundaries
11    // Left boundary: minimum possible time is 1
12    let left: bigint = 1n;
13  
14    // Right boundary: worst case is the fastest bus doing all trips alone
15    let right: bigint = BigInt(Math.min(...time)) * BigInt(totalTrips);
16  
17    // Binary search to find the minimum time
18    while (left < right) {
19        // Calculate middle point using bit shift for division by 2
20        const mid: bigint = (left + right) >> 1n;
21      
22        // Count total trips all buses can complete in 'mid' time
23        // For each bus, the number of trips = mid / time[i]
24        const totalCompletedTrips: bigint = time.reduce(
25            (accumulator: bigint, busTime: number) => accumulator + mid / BigInt(busTime), 
26            0n
27        );
28      
29        // Check if we can complete required trips in 'mid' time
30        if (totalCompletedTrips >= BigInt(totalTrips)) {
31            // Can complete required trips, try to find a smaller time
32            right = mid;
33        } else {
34            // Cannot complete required trips, need more time
35            left = mid + 1n;
36        }
37    }
38  
39    // Convert result back to number and return
40    return Number(left);
41}
42

Time and Space Complexity

The time complexity is O(n × log(m × k)), where:

  • n is the length of the array time (used in the sum computation within the key function)
  • m is the minimum value in the array time (found by min(time))
  • k is totalTrips

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. Off-by-One Error in Binary Search Range

The solution uses range(max_possible_time) which creates a range from [0, max_possible_time). This has two issues:

  • It starts from 0, but time 0 would result in 0 trips (division by bus times gives 0)
  • It excludes max_possible_time itself, which might be the actual answer

Why this is problematic: If the minimum time needed is exactly min(time) * totalTrips, the search will fail to find it since it's excluded from the range.

Solution:

def minimumTime(self, time: List[int], totalTrips: int) -> int:
    max_possible_time = min(time) * totalTrips
  
    # Include max_possible_time and start from 1
    return bisect_left(
        range(1, max_possible_time + 1), 
        totalTrips, 
        key=lambda x: sum(x // bus_time for bus_time in time)
    )

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 potentially overflow in some languages (though Python handles arbitrary precision).

Solution: Use a more conservative upper bound or implement manual binary search:

def minimumTime(self, time: List[int], totalTrips: int) -> int:
    left, right = 1, min(time) * totalTrips
  
    while left < right:
        mid = left + (right - left) // 2
        trips_completed = sum(mid // bus_time for bus_time in time)
      
        if trips_completed >= totalTrips:
            right = mid
        else:
            left = mid + 1
  
    return left

3. Misunderstanding bisect_left Behavior

bisect_left with a key function doesn't work as expected in older Python versions (before 3.10). The key parameter was only added in Python 3.10.

Solution for compatibility: Implement binary search manually or use a wrapper:

def minimumTime(self, time: List[int], totalTrips: int) -> int:
    def can_complete_trips(target_time):
        return sum(target_time // bus_time for bus_time in time) >= totalTrips
  
    left, right = 1, min(time) * totalTrips
  
    while left < right:
        mid = (left + right) // 2
        if can_complete_trips(mid):
            right = mid
        else:
            left = mid + 1
  
    return left

4. Edge Case: Single Bus

When there's only one bus, the calculation becomes straightforward but the binary search adds unnecessary complexity.

Optimization:

def minimumTime(self, time: List[int], totalTrips: int) -> int:
    if len(time) == 1:
        return time[0] * totalTrips
  
    # Continue with binary search for multiple buses
    ...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which technique can we use to find the middle of a linked list?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More