Problem overview
You get an int[] named cabTripTime, where element i is the number of minutes the i-th cab needs for one trip, and a number of trips n. The cabs drive at the same time, and each one starts its next trip as soon as it finishes the previous one. minTime returns the smallest number of minutes after which at least n trips are done in total.
For n = 3 and cabTripTime = [1, 2] the answer is 2. After 1 minute the first cab has finished 1 trip and the second none. After 2 minutes the first has finished 2 and the second 1, which makes 3. For n = 10 and [1, 3, 5, 7] the answer is 7: after 6 minutes the cabs have finished 6 + 2 + 1 + 0 = 9 trips, after 7 minutes 7 + 2 + 1 + 1 = 11.
A cab with a trip time of 0 finishes any number of trips instantly, so the answer is 0 then. The statement asks for O(m log T) time, where m is the number of cabs and T = n * min(cabTripTime), and for O(1) extra space.
Approach: binary search on the time
Handing out trips to cabs one at a time would take about n steps, and n can reach two million. Flip the question instead: given a time t, can the cabs finish n trips within t minutes? A cab with trip time v finishes t / v whole trips, because integer division drops an unfinished trip, and the cabs work in parallel, so their counts add up.
If t minutes are enough, so are t + 1, since cabs only finish more trips as time passes. The answers therefore run no, no, no, yes, yes, yes, and the task is to find the first yes. Binary search does this by testing the middle time and discarding the half that cannot hold the first yes.
The range is 1 to n * min(cabTripTime). The upper end always works: the fastest cab alone finishes n trips in that time. There are about log T rounds, and each one makes a pass over the m cabs.
For n = 3 and [1, 2] the range is 1 to 3. The middle, 2, gives 2 + 1 = 3 trips, which is enough, so record 2 and search 1 to 1. Time 1 gives 1 trip, which is not enough, so the answer is 2.
Step by step
The lesson provides minTime, which does the binary search, and asks you to write the helper it calls.
- Declare
boolean hasSolution(int[] cabTripTime, int n, int time)aboveminTime, and write its closing brace at once. It answers: can the cabs finishntrips withintimeminutes? - Create a counter
s, along, starting at 0. It counts the trips all cabs finish. Why it is alongis explained after the solution. - Open the loop over the cabs: a for-each over
cabTripTimewith anintnamedv, the minutes one trip of that cab takes. Write its closing brace at once. - Inside the loop, add
time / vtos. Dividing twoints drops the fraction, so a trip that isn't finished doesn't count. - After the loop, return
s >= n.truemeans thattimeis enough, so the search tries a shorter time.falsemeans it needs a longer one.
In minTime, the search finds the smallest trip time, sets left = 1 and right = n * minTime, and loops while left <= right. If the middle time is enough, it is recorded in result and the search continues on the shorter half (right = mid - 1), in case an earlier time also works. Otherwise it moves to the longer half (left = mid + 1). The last recorded time is the smallest one that works.
The solution in Java
class Solution {
boolean hasSolution(int[] cabTripTime, int n, int time) {
long s = 0;
for (int v : cabTripTime) {
s += time / v;
}
return s >= n;
}
public int minTime(int[] cabTripTime, int n) {
int minTime = Arrays.stream(cabTripTime).min().getAsInt();
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 Java details worth knowing:
- The counter is a
long. Anintthat goes past 2,147,483,647 wraps around to a negative number without any error. The counter adds up the trips of every cab, so it can get far beyond that. With a million cabs that each take 1 minute, a time of 1,000,000 minutes gives 1,000,000,000,000 trips: anintcounter ends that loop at -727379968, alongat 1000000000000. With those cabs andn = 2000000,minTimereturns 2, while the same code with anintcounter returns 1142464. - Mixed arithmetic. In
s += time / vthe division is done inint, which truncates, and the result is then widened tolongfor the addition. Ins >= n,nis widened tolongautomatically. - Division by zero throws. An
intdivision by 0 throws anArithmeticException. The solution never divides by a zero trip time: thenminTimeis 0, sorightis 0,left <= rightis false at once, and the method returns 0 without callinghasSolution. Arrays.stream(cabTripTime).min().getAsInt(). Streaming anint[]gives anIntStream. Itsmin()returns anOptionalInt, andgetAsInt()unwraps it. It would throw on an empty array, which the constraints rule out. The class needsjava.util.Arraysimported when you compile it as a standalone file.(left + right) >> 1halves a non-negativeintby shifting one bit. Withrightat most 200 million, the sum fits in anint.- Several declarations on one line.
int result = 0, left = 1, right = n * minTime;declares threeintvariables.
Complexity
Let m be the number of cabs and T = n * min(cabTripTime).
Time: O(m log T). The range has T values and halves every round, so there are about log T rounds. Each round calls hasSolution, which makes one pass over the m cabs. Finding the minimum first costs one more pass, O(m).
Space: O(1). The code keeps a handful of numbers and does not copy the array.
Edge cases and common mistakes
- A cab with trip time 0.
[0, 5]withn = 10returns 0. - One cab.
[5]withn = 4returns 20: the cab does its four trips one after another. - One trip.
[3, 3, 3]withn = 1returns 3. - Cabs that finish together.
[2, 3]withn = 5returns 6: after 6 minutes the cabs have done 3 + 2 = 5 trips, and after 5 minutes only 2 + 1 = 3. - Very many cabs. A million cabs with trip time 1 and
n = 2000000return 2. A counter that is too small wraps and breaks this case. - Stopping at the first
true. WhenhasSolutionsays yes, a smaller time may also work. Recordmidand keep searching the shorter half. - Counting a partial trip. A cab with trip time 3 has finished 1 trip at minute 5, not 2. Rounding up would count unfinished trips.
- Using
left < right. A range with a single value would never be tested. Keep<=.
Testing
n = 3,[1, 2]gives2.n = 10,[1, 3, 5, 7]gives7.n = 10,[0, 5]gives0.n = 4,[5]gives20.n = 5,[2, 3]gives6.n = 10000,[20000]gives200000000.n = 2000000with one million cabs that each take 1 minute gives2.
