{x}
blog image

Minimum Time to Complete Trips

You are given an array time where time[i] denotes the time taken by the ith bus to complete one trip.

Each bus can make multiple trips successively; that is, the next trip can start immediately after completing the current trip. Also, each bus operates independently; that is, the trips of one bus do not influence the trips of any other bus.

You are also given an integer totalTrips, which denotes the number of trips all buses should make in total. Return the minimum time required for all buses to complete at least totalTrips trips.

 

Example 1:

Input: time = [1,2,3], totalTrips = 5
Output: 3
Explanation:
- At time t = 1, the number of trips completed by each bus are [1,0,0]. 
  The total number of trips completed is 1 + 0 + 0 = 1.
- At time t = 2, the number of trips completed by each bus are [2,1,0]. 
  The total number of trips completed is 2 + 1 + 0 = 3.
- At time t = 3, the number of trips completed by each bus are [3,1,1]. 
  The total number of trips completed is 3 + 1 + 1 = 5.
So the minimum time needed for all buses to complete at least 5 trips is 3.

Example 2:

Input: time = [2], totalTrips = 1
Output: 2
Explanation:
There is only one bus, and it will complete its first trip at t = 2.
So the minimum time needed to complete 1 trip is 2.

 

Constraints:

  • 1 <= time.length <= 105
  • 1 <= time[i], totalTrips <= 107

Solution Explanation: Minimum Time to Complete Trips

This problem asks for the minimum time required for all buses to complete a specified number of total trips. Each bus takes a different amount of time to complete one trip, and buses operate independently. The key insight is that the problem exhibits monotonicity: if a certain time t is sufficient to complete all trips, then any time greater than t will also be sufficient. This property allows for an efficient solution using binary search.

  1. Initialization: We define a search space. The lower bound (left) is 1 (minimum possible time). The upper bound (right) is the minimum bus time multiplied by totalTrips. This is an upper bound because even if only the slowest bus is used, this amount of time is enough to fulfill the condition.

  2. Binary Search Iteration: The core of the algorithm is a binary search loop. In each iteration:

    • We calculate the mid point of the search space.
    • We simulate the number of completed trips for each bus at time mid. This is done by integer division (mid / time[i]), which gives the number of complete trips each bus can make within time mid. We sum the trips across all buses.
    • Decision: If the total number of completed trips is greater than or equal to totalTrips, it means mid is a valid time, and we can potentially find a smaller time. We update right to mid. Otherwise, we need a longer time, so we update left to mid + 1.
  3. Termination: The loop continues until left and right converge. At this point, left holds the minimum time required.

Time Complexity Analysis

The binary search takes O(log R), where R is the range of the search space (which is approximately min(time) * totalTrips). Inside the binary search loop, we iterate through the time array once to calculate the total number of trips. Therefore, the overall time complexity is O(n * log(min(time) * totalTrips)), where n is the length of the time array.

Space Complexity Analysis

The algorithm uses a constant amount of extra space to store variables like left, right, mid, and totalTrips. Therefore, the space complexity is O(1).

Code Examples (Multiple Languages)

The code examples below demonstrate the binary search solution in several programming languages. They closely follow the algorithm explained above.

Python:

import bisect
 
class Solution:
    def minimumTime(self, time: List[int], totalTrips: int) -> int:
        left = 1
        right = min(time) * totalTrips  # Upper bound
        while left < right:
            mid = (left + right) // 2
            total_trips_completed = sum(mid // t for t in time)
            if total_trips_completed >= totalTrips:
                right = mid
            else:
                left = mid + 1
        return left
 

Java:

class Solution {
    public long minimumTime(int[] time, int totalTrips) {
        long left = 1;
        long right = (long) Arrays.stream(time).min().getAsInt() * totalTrips; 
        while (left < right) {
            long mid = left + (right - left) / 2;
            long totalTripsCompleted = 0;
            for (int t : time) {
                totalTripsCompleted += mid / t;
            }
            if (totalTripsCompleted >= totalTrips) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

C++:

class Solution {
public:
    long long minimumTime(vector<int>& time, int totalTrips) {
        long long left = 1;
        long long right = (long long) *min_element(time.begin(), time.end()) * totalTrips;
        while (left < right) {
            long long mid = left + (right - left) / 2;
            long long totalTripsCompleted = 0;
            for (int t : time) {
                totalTripsCompleted += mid / t;
            }
            if (totalTripsCompleted >= totalTrips) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
};

JavaScript:

/**
 * @param {number[]} time
 * @param {number} totalTrips
 * @return {number}
 */
var minimumTime = function(time, totalTrips) {
    let left = 1;
    let right = Math.min(...time) * totalTrips;
    while (left < right) {
        let mid = Math.floor((left + right) / 2);
        let completedTrips = 0;
        for (let t of time) {
            completedTrips += Math.floor(mid / t);
        }
        if (completedTrips >= totalTrips) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
};

These code examples all implement the same binary search approach, with minor syntactic differences depending on the language. Remember to handle potential integer overflow issues (especially in languages with limited integer size) when dealing with large inputs. Using long long in C++ and BigInt in JavaScript (if necessary) can help prevent this.