818. Race Car

https://leetcode.com/problems/race-car/submissions/

初始位置为0,速度为1,每步可以速度*2, 或者调头并把速度置为1,问最后到target所需最少步数。最小步数问题用BFS或DP。BFS需要做一些剪枝,比如visited只存speed为1或-1的key = pos_speed,虽然会重复走,但会大规模减少visited的size,从而降低检索时间;还有当距离为target两倍时,终止继续遍历。DP则把对每个距离i, 遍历所有可能性,主要是两种: 1. 加速到j, (调两次头)速度重置回1,再加速到k位置,对剩下的i - (j-k)距离用子问题求解; 2. 一直加速到刚好超过i的位置,然后回头到i。看上去k的那个循环仿佛是多余的,但其实是需要的,在对j做循环时不能直接用dp[i - j],原因我目前也不知道。

答案参考了

/*
 * @lc app=leetcode id=818 lang=cpp
 *
 * [818] Race Car
 */
class Solution {
public:
    int racecar(int target) {
       vector<int> dp(target + 1);
       for (int i = 1; i <= target; ++i) {
           dp[i] = INT_MAX; // 跑完i距离需要的最短步数
           int j = 1, cnt1 = 1;
           // 正向不断加速到i前的每个可能的j位置(最远到2^N - 1)
           for (; j < i; j = (1 << ++cnt1) - 1) {
               // 调头再调回来让速度reset回1,不断正向加速到k位置, 再解决子问题: 跑(i - (j - k))需要的最少步数
               for (int k = 0, cnt2 = 0; k < j; k = (1 << ++cnt2) - 1) {
                   dp[i] = min(dp[i], cnt1 + 1 + cnt2 + 1 + dp[i - (j - k)]);
               }
           }
           // 此时j所在为刚好正向跑过i, 再回头跑到i
           dp[i] = min(dp[i], cnt1 + (i == j ? 0 : 1) + dp[j - i]);
       } 
       return dp[target];
    }
};

Last updated