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.
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.
The problem can be solved efficiently using dynamic programming. Let's break down the steps:
i
and lane l
(1, 2, or 3), keep track of the minimum jumps needed to reach that state.
n
) is the answer.
This approach ensures that every state is visited only once, making the solution efficient.
Let's use the sample input obstacles = [0,1,2,3,0]
.
The optimal path is: Stay in lane 2 until blocked, jump to lane 3, then to lane 1, for a total of 2 jumps.
This makes the DP approach highly efficient and suitable for large input sizes.
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.
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);
};