Car Fleet II

hard monotonic stack math

Problem

There are n cars on a one-lane road, all driving in the same direction. cars[i] = [position, speed] lists them rear to front with strictly increasing positions. When a car catches up to the car (or fleet) directly ahead, they collide, merge into one fleet, and continue at the slower car's speed. For each car, return the time at which it first collides with the car in front of it, or -1 if that never happens.

Scanning right to left, keep a stack of cars ahead that could still be hit: pop a car if it is at least as fast as you, or if it collides and slows down before you would ever reach it.

Inputcars = [[1,2],[2,1],[4,3],[7,2]]
Output[1.00, -1.00, 3.00, -1.00]
Car 0 closes the 1-unit gap to car 1 at relative speed 2−1 = 1 → t = 1. Car 2 closes 3 units on car 3 at relative speed 3−2 = 1 → t = 3. Cars 1 and 3 never catch anyone.

def get_collision_times(cars):
    n = len(cars)
    ans = [-1.0] * n
    stack = []                # cars ahead that could still be hit
    for i in range(n - 1, -1, -1):
        p, s = cars[i]
        while stack:
            j = stack[-1]
            pj, sj = cars[j]
            if s <= sj or (ans[j] > 0 and (pj - p) / (s - sj) >= ans[j]):
                stack.pop()   # j is uncatchable or gone too soon
            else:
                break
        if stack:
            j = stack[-1]
            ans[i] = (cars[j][0] - p) / (s - cars[j][1])
        stack.append(i)
    return ans
function getCollisionTimes(cars) {
  const n = cars.length;
  const ans = new Array(n).fill(-1);
  const stack = []; // cars ahead that could still be hit
  for (let i = n - 1; i >= 0; i--) {
    const p = cars[i][0], s = cars[i][1];
    while (stack.length) {
      const j = stack[stack.length - 1];
      const pj = cars[j][0], sj = cars[j][1];
      if (s <= sj || (ans[j] > 0 && (pj - p) / (s - sj) >= ans[j])) {
        stack.pop(); // j is uncatchable or gone too soon
      } else {
        break;
      }
    }
    if (stack.length) {
      const j = stack[stack.length - 1];
      ans[i] = (cars[j][0] - p) / (s - cars[j][1]);
    }
    stack.push(i);
  }
  return ans;
}
public double[] getCollisionTimes(int[][] cars) {
    int n = cars.length;
    double[] ans = new double[n];
    Arrays.fill(ans, -1.0);
    Deque<Integer> stack = new ArrayDeque<>(); // cars ahead
    for (int i = n - 1; i >= 0; i--) {
        int p = cars[i][0], s = cars[i][1];
        while (!stack.isEmpty()) {
            int j = stack.peek();
            int pj = cars[j][0], sj = cars[j][1];
            if (s <= sj || (ans[j] > 0 && (double) (pj - p) / (s - sj) >= ans[j])) {
                stack.pop(); // j is uncatchable or gone too soon
            } else {
                break;
            }
        }
        if (!stack.isEmpty()) {
            int j = stack.peek();
            ans[i] = (double) (cars[j][0] - p) / (s - cars[j][1]);
        }
        stack.push(i);
    }
    return ans;
}
vector<double> getCollisionTimes(vector<vector<int>>& cars) {
    int n = cars.size();
    vector<double> ans(n, -1.0);
    vector<int> stack; // cars ahead that could still be hit
    for (int i = n - 1; i >= 0; i--) {
        int p = cars[i][0], s = cars[i][1];
        while (!stack.empty()) {
            int j = stack.back();
            int pj = cars[j][0], sj = cars[j][1];
            if (s <= sj || (ans[j] > 0 && double(pj - p) / (s - sj) >= ans[j])) {
                stack.pop_back(); // j is uncatchable or gone too soon
            } else {
                break;
            }
        }
        if (!stack.empty()) {
            int j = stack.back();
            ans[i] = double(cars[j][0] - p) / (s - cars[j][1]);
        }
        stack.push_back(i);
    }
    return ans;
}
Time: O(n) Space: O(n)