818. Race Car

Your car starts at position 0 and speed +1 on an infinite number line. (Your car can go into negative positions.)

Your car drives automatically according to a sequence of instructions A (accelerate) and R (reverse).

When you get an instruction "A", your car does the following: position += speed, speed *= 2.

When you get an instruction "R", your car does the following: if your speed is positive then speed = -1 , otherwise speed = 1. (Your position stays the same.)

For example, after commands "AAR", your car goes to positions 0->1->3->3, and your speed goes to 1->2->4->-1.

Now for some target position, say the length of the shortest sequence of instructions to get there.

Example 1:
Input: 
target = 3
Output: 2
Explanation: 
The shortest instruction sequence is "AA".
Your position goes from 0->1->3.
Example 2:
Input: 
target = 6
Output: 5
Explanation: 
The shortest instruction sequence is "AAARA".
Your position goes from 0->1->3->7->7->6.

Note:

  • 1 <= target <= 10000.

// BFS
int racecar(int target) { // time: O(target * log(target)); space: O(target * log(target))
    int res = 0;
    queue<pair<int, int> > q({{0, 1}}); // pair<position, speed>
    unordered_set<string> visited({"0_1"});
    while (!q.empty()) {
        for (int i = q.size() - 1; i >= 0; --i) {
            int pos = q.front().first, speed = q.front().second;
            q.pop();
            if (pos == target) return res;
            // Acceleration
            int new_pos = pos + speed, new_speed = speed * 2;
            string encode_str = to_string(new_pos) + "_" + to_string(new_speed);
            if (!visited.count(encode_str) && new_pos > 0 && new_pos < 2 * target) {
                visited.insert(encode_str);
                q.push({new_pos, new_speed});
            }
            // Reverse
            new_pos = pos;
            new_speed = (speed > 0) ? -1 : 1;
            encode_str = to_string(new_pos) + "_" + to_string(new_speed);
            if (!visited.count(encode_str) && new_pos > 0 && new_pos < 2 * target) {
                visited.insert(encode_str);
                q.push({new_pos, new_speed});
            }
        }
        ++res;
    }
    return -1;
}
// Top-Down Dynamic Programming
int helper(int i, vector<int>& dp) {
    if (dp[i] >= 0) return dp[i];
    dp[i] = numeric_limits<int>::max();
    int j = 1, m = 1;
    // Consider the case j < i then reverse directly after continuous m acceleration
    // Then reverse again after continuous acceleration (p is the reverse distance after the first reverse)
    for (; j < i; j = (1 << ++m) - 1) {
        for (int q = 0, p = 0; p < j; p = (1 << ++q) - 1) {
            dp[i] = min(dp[i], m + 1 + q + 1 + helper(i - (j - p), dp) );
        }
    }
    // Consider the case j == i or j > i then reverse
    dp[i] = min(dp[i], m + (j == i ? 0 : 1 + helper(j - i, dp) ) );
    return dp[i];
}
int racecar(int target) { // time: O(target * (log(target))^2); space: O(target)
    vector<int> dp(target + 1, -1);
    dp[0] = 0;
    return helper(target, dp);
}
// Bottom-Up Dynamic Programming
int racecar(int target) { // time: O(target * (log(target))^2); space: O(target)
    vector<int> dp(target + 1, 0);
    for (int i = 1; i <= target; ++i) {
        dp[i] = numeric_limits<int>::max();
        int j = 1, m = 1;
        // Consider the case j < i then reverse directly after continuous m acceleration
        // Then reverse again after continuous acceleration (p is the reverse distance after the first reverse)
        for (; j < i; j = (1 << ++m) - 1) {
            for (int q = 0, p = 0; p < j; p = (1 << ++q) - 1) {
                dp[i] = min(dp[i], m + 1 + q + 1 + dp[i - (j - p)]);
            }
        }
        // Consider the case j == i or j > i then reverse
        dp[i] = min(dp[i], m + (j == i ? 0 : 1 + dp[j - i]));
    }
    return dp.back();
}

Last updated

Was this helpful?