Problem overview
The method CarFleet receives the target mile of a one-lane road and two int[] arrays: position[i] is the mile where car i starts, and speed[i] its speed in miles per hour. Cars can't pass each other. A car that catches up with a slower car or fleet ahead of it slows down and drives on with it, bumper to bumper. A group of cars driving together is a fleet, and a lone car is a fleet too. A car that reaches a fleet exactly at target is part of it. The method returns how many fleets arrive.
In the first example, target = 12, position = {10, 8, 0, 5, 3} and speed = {2, 4, 1, 1, 3}. The cars from miles 10 and 8 both arrive after 1 hour (the second catches the first exactly at the target): one fleet. The car from mile 3 catches the car from mile 5, and they drive on at speed 1: a second fleet. The car from mile 0 never catches anyone: a third. The answer is 3.
Approach: sort by position, then compare arrival times
Only the order of the cars on the road matters. Visit them from the one closest to the target to the one farthest from it. Driving alone and unblocked, car i would arrive after (target - position[i]) / speed[i] hours. The car closest to the target can never be blocked, so it leads the first fleet.
For every later car, look at the fleet just ahead of it. A fleet arrives when its leading car would arrive alone, since that car is never slowed down. If the new car would arrive no later than that, it catches up with the fleet at the latest at the target, and joins it. If it would arrive later, it never catches up, so it starts a new fleet, which the cars behind it may still join.
The fleet just ahead is always the most recent one, so a stack of fleets is enough: compare each car with the top only. Each fleet is stored as the number of the car that leads it. The answer is the size of the stack.
Here is what happens for the first example. The cars are visited in the order 0, 1, 3, 4, 2:
car 0 (mile 10, speed 2) alone: 1 h -> new fleet, fleets=1
car 1 (mile 8, speed 4) alone: 1 h -> joins, fleets=1
car 3 (mile 5, speed 1) alone: 7 h -> new fleet, fleets=2
car 4 (mile 3, speed 3) alone: 3 h -> joins, fleets=2
car 2 (mile 0, speed 1) alone: 12 h -> new fleet, fleets=3
Car 4 would arrive after 3 hours, earlier than the 7 hours of the fleet ahead, so it joins. Car 1 ties with car 0 at 1 hour and joins as well: arriving at the same moment still means one fleet, so the comparison must be strict.
Step by step
- Store the number of cars,
position.Length, inn. - Create
order, anint[]of sizen. It will hold car numbers sorted by position. Sorting the numbers, not the positions, keeps each position together with its speed. - Fill it with a loop:
order[i] = i. - Sort it with
Array.Sort(order, (a, b) => position[b] - position[a]). The lambda gets two car numbers and returns a negative number when caramust come first. That happens whenahas the larger position, so the car closest to the target comes first. - Create the stack
fleets, aStack<int>of lead-car numbers. - Visit the cars in sorted order with
foreach. - Start a new fleet when the stack is empty or the car would arrive later than the fleet on top:
fleets.Count == 0 || ArrivesLater(...). The||stops at the first true part, soPeek()is only called on a non-empty stack. Otherwise do nothing: the car joins the top fleet. - Push the car with
fleets.Push(car). - Return
fleets.Count. Every car either started a fleet or joined one, so each fleet is on the stack once. - Write the helper
ArrivesLater, which answers whethercar, driving alone, would arrive strictly later thanlead. - Compare without dividing. The time of
caris greater than the time ofleadexactly when(target - position[car]) * speed[lead]is greater than(target - position[lead]) * speed[car]. Both speeds are positive, so multiplying both sides by them keeps the comparison the same. The products are computed in 64 bits.
The solution in C#
class Solution {
public int CarFleet(int target, int[] position, int[] speed) {
int n = position.Length;
int[] order = new int[n];
for (int i = 0; i < n; i++) {
order[i] = i;
}
Array.Sort(order, (a, b) => position[b] - position[a]);
Stack<int> fleets = new Stack<int>();
foreach (int car in order) {
if (fleets.Count == 0 || ArrivesLater(target, position, speed, car, fleets.Peek())) {
fleets.Push(car);
}
}
return fleets.Count;
}
bool ArrivesLater(int target, int[] position, int[] speed, int car, int lead) {
return (long) (target - position[car]) * speed[lead] > (long) (target - position[lead]) * speed[car];
}
}
The C# details worth knowing:
- Why
(long).target - position[car]can be close to 1,000,000 and a speed can be 1,000,000, so a product can reach 10^12, far beyond what anintholds (about 2.1 billion). In C#, the type of a multiplication comes from its operands:int * intis computed in 32 bits and wraps silently. Witha = 1000000andb = 1000000,a * bgives-727379968, while(long) a * bgives1000000000000. The cast applies to the first factor and makes the whole multiplication 64-bit, because the other factor is widened automatically. - The cast must come before the multiplication.
long product = a * b;still multiplies asints and widens the wrapped result afterwards: it also prints-727379968. Cast an operand, not the result. - The wrapped version gives wrong answers. For
target = 1000000,position = {0, 1}andspeed = {1, 1000000}, the method returns 2 withlongproducts and 1 if the same comparison is written with plainints. Array.Sortwith a lambda. The overloadArray.Sort(array, comparison)takes aComparison<T>, which a lambda(a, b) => ...converts to. The lambda returns a negative number, zero or a positive number. Subtracting positions is safe here, because positions are between 0 and 10^6. With values nearint.MinValueorint.MaxValue, a subtraction could overflow, andCompareTowould be the safe choice.- The lambda uses the method's arrays. It reads
positionfrom the enclosing method (a closure), which is how the sort orders car numbers by a value stored elsewhere. - The helper is private. A method written without an access modifier is
privatein a class, which is enough here, since onlyCarFleetcalls it. Stack<T>comes withPush,Peek(look at the top without removing it),PopandCount. It lives inSystem.Collections.Generic, andArrayinSystem: in a file of your own, add bothusinglines.
Complexity
Time: O(n log n). Sorting the cars takes O(n log n). The loop after it does a constant amount of work per car, O(n) in total, so sorting dominates.
Space: O(n). The order array and the stack each hold at most n cars.
Edge cases and common mistakes
- One car.
target = 10,position = {3},speed = {3}gives 1. - Cars given out of order. The input isn't sorted by position.
position = {0, 4, 2}withspeed = {2, 1, 3}andtarget = 10gives 1; the sort makes the visiting order right. - Same arrival time. A strict
>in the comparison keeps cars that tie with the fleet ahead in that fleet. Using>=would split the example above into more fleets. - A faster car behind a slower one.
target = 100,position = {0, 2, 4},speed = {4, 2, 1}gives 1: the cars merge one after another and all arrive together. - A slower car behind.
target = 10,position = {0, 5},speed = {1, 1}gives 2: the car behind never catches up. Withspeed = {10, 1}it gives 1. - Dividing integers.
(target - position) / speedwithints truncates, so 10 / 3 becomes 3 and two different arrival times can look equal. Dividing asdoubleavoids that for most inputs, but comparing products is exact. - Sorting the positions alone. Positions and speeds would no longer match. Sorting car numbers keeps each car's two values together.
- Comparing with the wrong fleet. A car must be compared with the fleet on top of the stack, not with the car just before it in the order: a car that joined a fleet is no longer the one that sets its arrival time.
Testing
target = 12,position = {10, 8, 0, 5, 3},speed = {2, 4, 1, 1, 3}gives3.target = 10,position = {3},speed = {3}gives1.target = 100,position = {0, 2, 4},speed = {4, 2, 1}gives1.target = 10,position = {0, 4, 2},speed = {2, 1, 3}gives1.target = 10,position = {6, 8},speed = {3, 2}gives2.target = 10,position = {0, 5},speed = {1, 1}gives2.target = 1000000,position = {0, 1},speed = {1, 1000000}gives2.
