We use cookies (including Google cookies) to personalize ads and analyze traffic. By continuing to use our site, you accept our Privacy Policy.

Race Car

Number: 836

Difficulty: Hard

Paid? No

Companies: Google, Turing, Amazon, Bloomberg


Problem Description

A car starts at position 0 with a speed of +1. It can only execute two types of instructions: "A" (accelerate) and "R" (reverse). When executing "A", the car moves forward a distance equal to its current speed and then doubles its speed. When executing "R", the car reverses its direction (speed becomes -1 if positive, and +1 if negative) without changing its position. Given a target position, determine the minimum number of instructions required to reach that target.


Key Insights

  • The problem involves non-linear moves where each accelerate ("A") exponentially increases the car's speed.
  • You can overshoot the target and then reverse, so both overshoot and undershoot strategies must be considered.
  • Dynamic Programming (DP) or memoized recursion can be used to avoid repeated work.
  • For a given target, determine the smallest number of accelerations (k) such that (2^k - 1) is greater than or equal to the target.
  • There are two cases: one where you exactly land on the target and another where you must reverse (or partially reverse) to correct the overshoot.

Space and Time Complexity

Time Complexity: O(target * log(target)) in the worst case due to recursive calls and iterating over possible reverse moves. Space Complexity: O(target), due to the memoization cache used to store intermediate results.


Solution

We use a memoized recursive (DP) strategy. For a given target, we compute the minimal sequence of instructions as follows:

  1. Find the smallest k such that (2^k - 1) ≥ target. If (2^k - 1) exactly equals the target, then k instructions ("A" repeated k times) are enough.
  2. Otherwise, consider:
    • Overshooting: perform k accelerations to overshoot, then reverse, and solve the remaining distance recursively.
    • Partial reverse: for each m in range 0 to k-2, perform (k-1) accelerations, then a reverse, then m accelerations in the opposite direction, reverse again and solve for the remaining distance.
  3. Choose the sequence with the minimum total instructions. This approach uses recursion with memoization to avoid recomputation and carefully explores both overshoot and partial reversal scenarios.

Code Solutions

class Solution:
    def racecar(self, target: int) -> int:
        # Dictionary to store computed results for targets to avoid recomputation.
        memo = {}
        
        def dp(t):
            # Base case: if target is 0, no instructions are required.
            if t == 0:
                return 0
            # If result for target t is already computed, return it.
            if t in memo:
                return memo[t]
            # Determine the minimal number of accelerations (k) such that (2^k - 1) >= t.
            k = 1
            while (1 << k) - 1 < t:
                k += 1
            # If we exactly reach the target with k accelerations, that's the answer.
            if (1 << k) - 1 == t:
                memo[t] = k
                return k
            # Option 1: overshoot the target, then reverse and recursively solve for the overshoot difference.
            ans = k + 1 + dp((1 << k) - 1 - t)
            # Option 2: try partial reversal before reaching the k-th acceleration.
            for m in range(k - 1):
                # Position after (k-1) accelerations, then reverse for m accelerations in the opposite direction.
                pos = (1 << (k - 1)) - 1 - ((1 << m) - 1)
                # Total instructions: (k-1) accelerations, one reverse, m accelerations, another reverse, and then finish.
                ans = min(ans, (k - 1) + 1 + m + 1 + dp(t - pos))
            memo[t] = ans
            return ans

        return dp(target)
← Back to All Questions