Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1824. Minimum Sideway Jumps - Leetcode Solution

Problem Description

You are given an array obstacles of length n + 1 where obstacles[i] is either 0 (no obstacle) or an integer from 1 to 3 representing a lane with an obstacle at position i. There are three lanes (numbered 1, 2, 3), and you start at position 0 in lane 2. At each step, you can either move forward in the same lane if there is no obstacle, or perform a sideway jump to another lane at the same position (which counts as one jump), as long as that lane does not have an obstacle at that position.

Your goal is to reach position n (the end of the track) with the minimum number of sideway jumps. The constraints guarantee at least one valid solution exists.

  • You cannot move to a lane with an obstacle at your current position.
  • You can perform sideway jumps at any position, but only to lanes without obstacles at that position.
  • Find the minimum number of sideway jumps needed to reach the end.

Thought Process

At first glance, a brute-force approach might seem reasonable: at each step, try all possible moves (forward or sideway jumps) and recursively explore every path to the end. However, this would result in a huge number of repeated calculations, especially as the number of positions increases.

To optimize, notice that at each position and lane, the minimum number of jumps to reach the end can be determined and reused if we encounter the same state again. This is a classic case for dynamic programming (DP).

We can represent our state as a combination of (position, lane), and for each state, compute the minimum jumps needed. By storing and reusing these results, we can avoid redundant work, making the solution efficient.

Solution Approach

The problem can be solved efficiently using dynamic programming. Let's break down the steps:

  1. State Representation: For each position i and lane l (1, 2, or 3), keep track of the minimum jumps needed to reach that state.
  2. Initialization: At position 0, you start in lane 2 with 0 jumps. To be in lane 1 or 3 at position 0, you would have needed 1 sideway jump from lane 2.
  3. Transition:
    • If moving forward in the same lane is possible (no obstacle), carry over the jump count.
    • If there is an obstacle, consider sideway jumps to other lanes at the same position, but only to lanes without obstacles at that position. Increment the jump count by 1 for each sideway jump.
  4. Iterative DP:
    • Iteratively update the minimum jumps for each lane at each position, using the previous position's results and considering sideway jumps if blocked.
    • At each position, for each lane, check if a sideway jump would result in fewer jumps than moving forward.
  5. Result: The minimum jumps among all lanes at the last position (position n) is the answer.

This approach ensures that every state is visited only once, making the solution efficient.

Example Walkthrough

Let's use the sample input obstacles = [0,1,2,3,0].

  • We start at position 0, lane 2 (no obstacle), 0 jumps.
  • At position 1: Lane 1 has an obstacle, so only lanes 2 and 3 are available. Move forward in lane 2 (no jump), or perform a sideway jump to lane 3 (1 jump).
  • At position 2: Lane 2 has an obstacle, so now only lanes 1 and 3 are available. If you were in lane 2, you must sideway jump to lane 1 or lane 3 (1 jump). If you were already in lane 3, move forward (no jump).
  • At position 3: Lane 3 has an obstacle, so only lanes 1 and 2 are available. Continue moving or sideway jump as needed.
  • At position 4: No obstacles, so all lanes are available. The minimum number of jumps needed to reach this point is 2.

The optimal path is: Stay in lane 2 until blocked, jump to lane 3, then to lane 1, for a total of 2 jumps.

Time and Space Complexity

  • Brute-force approach: Exponential time complexity, as it explores all possible paths (O(3^n)), making it infeasible for large inputs.
  • Optimized DP approach: Time complexity is O(n), as there are at most 3 lanes and n positions, and each state is computed once. Space complexity is also O(n) (or O(1) if we optimize to use only the current and previous states).

This makes the DP approach highly efficient and suitable for large input sizes.

Summary

The Minimum Sideway Jumps problem is a classic dynamic programming challenge. By representing the problem as a state machine (position and lane), and iteratively updating the minimum jumps required while avoiding obstacles, we achieve an efficient solution. The key insight is to avoid redundant calculations by storing and reusing results for each state. This approach is both elegant and highly performant.

Code Implementation

class Solution:
    def minSideJumps(self, obstacles):
        n = len(obstacles) - 1
        INF = float('inf')
        # dp[lane]: min jumps to reach current position in lane (1-indexed)
        dp = [1, 0, 1]  # At pos 0: lane 1:1, lane 2:0, lane 3:1
        for i in range(1, n + 1):
            for l in range(3):
                if obstacles[i] == l + 1:
                    dp[l] = INF  # Can't be in this lane
            for l in range(3):
                if obstacles[i] != l + 1:
                    for k in range(3):
                        if l != k and obstacles[i] != k + 1:
                            dp[l] = min(dp[l], dp[k] + 1)
        return min(dp)
      
class Solution {
public:
    int minSideJumps(vector<int>& obstacles) {
        int n = obstacles.size() - 1;
        const int INF = 1e9;
        vector<int> dp = {1, 0, 1}; // lanes 1,2,3
        for (int i = 1; i <= n; ++i) {
            for (int l = 0; l < 3; ++l) {
                if (obstacles[i] == l + 1)
                    dp[l] = INF;
            }
            for (int l = 0; l < 3; ++l) {
                if (obstacles[i] != l + 1) {
                    for (int k = 0; k < 3; ++k) {
                        if (l != k && obstacles[i] != k + 1)
                            dp[l] = min(dp[l], dp[k] + 1);
                    }
                }
            }
        }
        return min({dp[0], dp[1], dp[2]});
    }
};
      
class Solution {
    public int minSideJumps(int[] obstacles) {
        int n = obstacles.length - 1;
        int INF = (int)1e9;
        int[] dp = {1, 0, 1}; // lanes 1,2,3
        for (int i = 1; i <= n; i++) {
            for (int l = 0; l < 3; l++) {
                if (obstacles[i] == l + 1)
                    dp[l] = INF;
            }
            for (int l = 0; l < 3; l++) {
                if (obstacles[i] != l + 1) {
                    for (int k = 0; k < 3; k++) {
                        if (l != k && obstacles[i] != k + 1)
                            dp[l] = Math.min(dp[l], dp[k] + 1);
                    }
                }
            }
        }
        return Math.min(dp[0], Math.min(dp[1], dp[2]));
    }
}
      
var minSideJumps = function(obstacles) {
    const n = obstacles.length - 1;
    const INF = 1e9;
    let dp = [1, 0, 1]; // lane 1, 2, 3
    for (let i = 1; i <= n; i++) {
        for (let l = 0; l < 3; l++) {
            if (obstacles[i] === l + 1)
                dp[l] = INF;
        }
        for (let l = 0; l < 3; l++) {
            if (obstacles[i] !== l + 1) {
                for (let k = 0; k < 3; k++) {
                    if (l !== k && obstacles[i] !== k + 1)
                        dp[l] = Math.min(dp[l], dp[k] + 1);
                }
            }
        }
    }
    return Math.min(...dp);
};