Leetcode 818. Race Car

Problem Explanation

The problem involves a car on an infinite number line and a series of commands it can execute, including accelerating (A) and reversing (R). When the car accelerates, its speed doubles and it moves forward by the amount of its speed. When the car reverses, its speed is set to -1 if it was positive or 1 if it was negative. The goal is to determine the shortest sequence of commands that will get the car to a certain target position.

For example, if the target is 3, the shortest sequence of commands is "AA". The car's sequence of positions will be 0->1->3 while its speed sequence will be 1->2->4->-1.

If the target is 6, the shortest sequence is "AAARA". The car's sequence of positions in this case will be: 0->1->3->7->7->6.

The challenge is to implement a function that will find the shortest sequence of commands given a target from 1 to 10000.

Solution Approach

The approach to solve this problem is a combination of breadth-first search and dynamic programming. We initialize a 1D dp array where dp[i] represents the minimum number of commands to reach the position i. We then follow a recursive approach where we calculate the least number of commands required to reach the position i.

In the recursive function, we iterate over all possible scenarios which include reaching the target i by either overshooting or undershooting, and calculate the minimum steps required.

Optimizerization is achieved by storing and re-using the result of sub-problems in the dp array to avoid calculating them again, which is the principle of dynamic programming.

Python Solution

1
2python
3class Solution:
4    
5    def __init__(self):
6        self.dp = {}
7
8    def racecar(self, target: int) -> int:
9        if target in self.dp: 
10            return self.dp[target]
11        n = target.bit_length()
12        if (1 << n) - 1 == target:
13            self.dp[target] =  n
14        else:
15            self.dp[target] = self.racecar((1 << n) - 1 - target) + n + 1
16            for m in range(n-1):
17                cur = (1 << (n-1)) - (1 << m) - 1
18                self.dp[target] = min(self.dp[target], self.racecar(target - cur) + n + m + 1)
19        return self.dp[target]       

Java Solution

1
2java
3public class Solution {
4    int[] dp = new int[10001];
5    public int racecar(int target) {
6        if (dp[target] > 0) return dp[target];
7        int n = (int)(Math.log(target) / Math.log(2)) + 1;
8        if (1 << n == target + 1) dp[target] = n;
9        else {
10            dp[target] = racecar((1 << n) - 1 - target) + n + 1;
11            for (int m = 0; m < n - 1; ++m)
12                dp[target] = Math.min(dp[target], racecar(target - (1 << (n - 1)) + (1 << m)) + n + m + 1);
13        }
14        return dp[target];
15    }
16}

JavaScript Solution

1
2javascript
3var racecar = function(target) {
4    let dp = new Array(target + 1). fill(0);
5    for (let t = 1; t <= target; t++) {
6        dp[t] = Infinity;
7        let m = 1, j = 1;
8        for (; j < t; j = (1 << ++m) - 1)
9            for (let q = 0, p = 0; p < j; p = (1 << ++q) - 1)
10                dp[t] = Math.min(dp[t], m + 1 + q + 1 + dp[t - (j - p)]);
11        dp[t] = Math.min(dp[t], m + (t == j ? 0 : 1 + dp[j - t]));
12    }
13    return dp[target];
14};

C++ Solution

1
2cpp
3class Solution {
4public:
5    int racecar(int target) {
6        vector<int> dp(target + 3, INT_MAX);
7        dp[0] = 0, dp[1] = 1, dp[2] = 4;
8        for (int t = 3; t <= target; ++t) {
9            int m = 32 - __builtin_clz(t);
10            if (t == (1<<m) - 1) {
11                dp[t] = m;
12                continue;
13            }
14            for (int i = 0; i < m-1; ++i)
15                dp[t] = min(dp[t], dp[t - (1<<(m-1)) + (1<<i)] + m-1 + i + 2);
16            if ((1 << m) - 1 - t < t)
17                dp[t] = min(dp[t], dp[(1 << m) - 1 - t] + m + 1);
18        }
19        return dp[target];
20    }
21};

C# Solution

1
2csharp
3public class Solution {
4    public int Racecar(int target) {
5        int[] dp = new int[target + 1];
6        for (int i = 1; i <= target; i++) {
7            dp[i] = int.MaxValue;
8            int m = 1, j = 1;
9            for (; j < i; j = (1 << ++m) - 1)
10                for (int q = 0, p = 0; p < j; p = (1 << ++q) - 1)
11                    dp[i] = Math.Min(dp[i], m + 1 + q + 1 + dp[i - (j - p)]);
12            dp[i] = Math.Min(dp[i], m + (i == j ? 0 : 1 + dp[j - i]));
13        }
14        return dp[target];
15    }
16}

In each language solution, we first calculate how many accelerate "A" operations our car need to reach or overshoot the target. We also calculate the shortest sequence if our car reaches exactly to the target or overshoots by reversing once and moving to the target or overshoots and reverses twice to reach the target. In each case, we store the result in our dp array to optimize the solution for the sub-problem.## F# Solution

1
2fsharp
3let racecar target =
4    let dp = Array.create (target + 1) (System.Int32.MaxValue - 10)
5    dp.[0] <- 0
6    dp.[1] <- 1
7    let mutable t = 1
8    while t <= target do
9        let mutable m = 1
10        let mutable j = 0
11        while (1<<<m) - 1 <> t do
12            if (1<<<m) > t then
13                let rec setDP count =
14                    let j = (1<<<count)
15                    if dp.[t] > j + 1 + dp.[j-1] then
16                        dp.[t] <- j + 1 + dp.[j-1]
17                        setDP (count + 1)
18                    else
19                        ()
20                setDP 0
21            if (1<<<m) <= t then
22                if (1<<<m) - 1 = t then
23                    dp.[t] <- m
24                else
25                    let a = (1<<<m) - 1 - t
26                    if a < t then
27                        dp.[t] <- min dp.[t] (m + 1 + dp.[a])
28            m <- m + 1
29        j <- 1<<<m - 1
30        if dp.[t] = System.Int32.MaxValue - 10 then
31            dp.[t] <- m + (if j-t < t then dp.[j-t] else dp.[t])
32        t <- t + 1
33    dp.[target]

The F# solution for the shortest sequence of commands for a car on an infinite line is similar to the other language implementations. It includes the creation of an array dp where dp[i] represents the minimum number of commands to reach the position i. We iterate through this array and calculate the minimum steps required to reach the position i.

The main loop iterates through all the steps from 1 to the target position. The nested loop finds the shortest sequence of commands including reversing once or twice if it overshoots the target. The result is stored in the dp array for each position, which is then used to calculate the result for higher positions avoiding unnecessary calculations and thus optimizing the solution.

Conclusion

The problem of finding the shortest sequence of commands for a car on an infinite line to reach a target position can be solved efficiently by combining breadth-first search and dynamic programming. The key point is to make use of previous results instead of calculating them again in order to optimize the solution. These solutions can be implemented in multiple programming languages including Python, JavaScript, Java, C++, C#, and F#.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫