Start learning free

LESSON 282 OF 437 · BINARY SEARCH

Minimum Time for Cab Trips in C++: Binary Search on the Answer

Practice O(n log T) Read the full explanation

Uber has several cabs. The ith cab takes cabTripTime[i] minutes to complete any trip. Your task is to find the minimum time it will take Uber to get n trips completed with these cabs. You can assume there is no waiting time in-between the trips.

Note: Different cabs can take trips simultaneously (at any time, there can be more than one cab with an ongoing trip)

Example 1:

Input: n = 3, cabTripTime = [1, 2]
Output: 2
Explanation:
Trips can be managed like this:
- Trip 1: first cab from t = 0 to t = 1;
- Trip 2: second cab from t = 0 to t = 2;
- Trip 3: first cab from t = 1 to t = 2.

All the trips can be completed in 2 minutes. In 1 minute the cabs finish only 1 + 0 = 1 trip, so the answer is 2.

Example 2:

Input: n = 10, cabTripTime = [1, 3, 5, 7]
Output: 7
Explanation:
Trips can be managed like this:
- Trip 1: 1st cab from t = 0 to t = 1;
- Trip 2: 2nd cab from t = 0 to t = 3;
- Trip 3: 3rd cab from t = 0 to t = 5;
- Trip 4: 4th cab from t = 0 to t = 7;
- Trip 5: 1st cab from t = 1 to t = 2;
- Trip 6: 1st cab from t = 2 to t = 3;
- Trip 7: 1st cab from t = 3 to t = 4;
- Trip 8: 2nd cab from t = 3 to t = 6;
- Trip 9: 1st cab from t = 4 to t = 5;
- Trip 10: 1st cab from t = 5 to t = 6.

All the trips can be completed in 7 minutes. In 6 minutes the cabs finish at most 6 + 2 + 1 + 0 = 9 trips, so the answer is 7.

Constraints:

  • 1 <= cabTripTime.length <= 106
  • 0 <= cabTripTime[i] <= 2 * 104
  • 1 <= n <= 2 * 106
  • n * min(cabTripTime) <= 2 * 108
  • A cab whose trip time is 0 finishes any number of trips instantly, so then the answer is 0.

Note:

Your algorithm should run in O(m log T) time, where m is the number of cabs and T = n * min(cabTripTime) is the longest time worth trying, and use O(1) extra space.


STEP 1 OF 4
    Ctrl + Enter to check

    C++ · BINARY SEARCH

    Uber Rides in C++, explained

    Problem overview

    The function minTime receives a vector cabTripTime, where the i-th cab needs cabTripTime[i] minutes for any trip, and a number of trips n. It returns the least number of minutes in which the cabs can complete n trips together. Cabs work in parallel and don't wait between trips.

    For n = 3 and [1, 2], the answer is 2: in 2 minutes the first cab finishes 2 trips and the second finishes 1. In 1 minute they finish only 1. For n = 10 and [1, 3, 5, 7] the answer is 7: in 6 minutes the cabs finish 6 + 2 + 1 + 0 = 9 trips, and in 7 minutes 7 + 2 + 1 + 1 = 11.

    The statement sets O(m log T) time, where m is the number of cabs and T = n * min(cabTripTime), and O(1) extra space. A trip time of 0 means the answer is 0.

    Approach: binary search on the time

    Trying every time from 1 upward would take up to T checks. Instead, notice that the question "can all n trips be done within time minutes?" has a monotone answer. If time is enough, any larger time is enough too, because the cabs only get more minutes. So the answers over time look like no, no, no, yes, yes, yes, and the first yes can be found by binary search.

    The check itself is short. In time minutes, a cab with trip time v finishes time / v whole trips, rounding down. Add this up over all cabs and compare the total with n. That costs O(m) per check.

    For the upper end of the search, the fastest cab alone can do all n trips in n * min(cabTripTime) minutes, so the answer never exceeds that value. The search range is from 1 to T, which needs about log T checks, so the total is O(m log T).

    Step by step

    1. Write the check as a lambda, hasSolution, taking the vector, n and a time. It has no captures, so it receives everything as parameters.
    2. Start a counter s at 0 as the lambda's first line, as a long long (see the details below for why).
    3. Loop over the cabs with a range-based for and an int named v, the minutes one trip of that cab takes.
    4. Add time / v to s inside the loop: the number of whole trips this cab completes. Integer division in C++ drops the fraction.
    5. Return s >= n: enough trips fit within time.

    The lesson's solution then uses the lambda in an ordinary binary search: it finds the smallest trip time with min_element, sets left = 1 and right = n * minTime, and keeps the best feasible time in result. When mid works, the answer is mid or something smaller, so it records mid and searches the left half. When mid doesn't work, it searches the right half.

    The solution in C++

    class Solution {
    public:
        int minTime(vector<int>& cabTripTime, int n) {
            auto hasSolution = [](vector<int> &cabTripTime, int n, int time) {
                long long s = 0;
                for (int v : cabTripTime) {
                    s += time / v;
                }
                return s >= n;
            };
            
            int minTime = *min_element(cabTripTime.begin(), cabTripTime.end());
            int result = 0, left = 1, right = n * minTime;
            while (left <= right) {
                int mid = (left + right) >> 1;
                if (hasSolution(cabTripTime, n, mid)) {
                    result = mid;
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            return result;
        }
    };
    

    The C++ details worth a closer look:

    • Lambdas. auto hasSolution = [](...) { ... }; defines a function object in place. The empty brackets mean it captures nothing from the surrounding function, which is why the vector and n are passed in. The return type, bool, is deduced from return s >= n.
    • Parameters that hide outer names. The lambda's cabTripTime and n are its own parameters. Because it captures nothing, they can't clash with the outer ones. The local int minTime also has the same name as the method itself, which is allowed.
    • min_element returns an iterator. The * in front reads the value it points to. The call needs a non-empty range, which the constraints guarantee.
    • Why s is a long long. With 1,000,000 cabs of trip time 100 and n = 2000000, the first time the search tries is about 100,000,000 minutes, where the cabs finish about 10^12 trips: far more than the largest int, 2,147,483,647. A 64-bit counter holds that count, and with it this input returns the correct answer, 200. A 32-bit counter would overflow, which is undefined behaviour for signed integers in C++. right and mid stay int because the constraints keep them at or below about 4 * 10^8.
    • The midpoint. (left + right) >> 1 is the same as dividing by 2 for non-negative numbers. Here left + right is at most about 4 * 10^8, which fits in an int, since the constraints keep n * min(cabTripTime) at or below 2 * 10^8.
    • The shortcut for 0. If the smallest trip time is 0, then right is 0, the loop body never runs, and the function returns 0. That also means the division time / v never sees a zero divisor.
    • The unused-looking result = 0. It is the returned value when the loop doesn't run. When it does run, right = n * minTime is always feasible, so result is set at least once.

    Complexity

    Time: O(m log T). The search halves a range of size T, so it makes about log T checks, and each check loops over the m cabs. Finding the minimum adds one more pass, O(m).

    Space: O(1). A few integers; the vector is passed by reference and never copied.

    Edge cases and common mistakes

    • One cab. [4] with n = 3 gives 12: T = 12, and 11 minutes finish only 2 trips.
    • Equal cabs. [5, 5, 5] with n = 7 gives 15: three cabs finish 3 trips every 5 minutes, so 3 rounds cover 9 trips and 2 rounds only 6.
    • A zero trip time. [0, 5] with n = 4 gives 0. A search that started without taking the minimum first would divide by zero.
    • A time that stops mid-trip doesn't count. Rounding down is the point: time / v counts only completed trips.
    • Stopping at the first feasible time. A feasible mid may not be the smallest one. The search keeps going left to see whether something smaller also works.
    • A counter that is too small. The total number of trips over all cabs can pass what an int holds, so the counter s must be a long long. Comparing it with n is fine: the int is converted to long long first.

    Testing

    • n = 3, [1, 2] gives 2.
    • n = 10, [1, 3, 5, 7] gives 7.
    • n = 3, [4] gives 12.
    • n = 7, [5, 5, 5] gives 15.
    • n = 4, [0, 5] gives 0.
    • n = 2000000 and 1,000,000 cabs that all take 100 minutes gives 200.
    Keep going with C++About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free