题目:
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
.
思路:
采用动态规划的思路,定义dp[target]表示行驶长度为target的距离所需要的最小指示个数。可以证明有如下两种可能性:
1)如果target刚好是可以由“AAA...AR”(一共n步)达到,也就是说前面一路加速,最后再减速到-1,那么这种走法就是最优选择;
2)如果不满足条件1),那么最优解就有多种可能性了:a)第一次冲过target的时候进行‘R’操作,然后反向再接近target。此时我们已经走了n+1步,并且和target的距离已经减少到了(1 << n) - 1 - target,所以我们可以递归地调用rececar函数;b)前面先走m步(可以通过递归调用racecar函数得到最优值),然后再采用1)的策略。最终的答案就是a)和b)的各种可能性的最小值。
为了达到最优的运行效率,我们采用dp + memorization的策略:每当需要计算一个子问题时,首先查表看看该子问题是否已经被计算过了;如果是,则直接返回结果;否则我们再进行计算,并且将结果保存下来,以备后面的计算再次使用。
代码:
class Solution { public: int racecar(int target) { if (dp[target] > 0) { // already calculated before return dp[target]; } int n = floor(log2(target)) + 1, res; if (1 << n == target + 1) { // perfect solution: just use someting like "AAA...AR" dp[target] = n; } else { // need to go back using the same strategy dp[target] = racecar((1 << n) - 1 - target) + n + 1; for (int m = 0; m < n - 1; ++m) { dp[target] = min(dp[target], racecar(target - (1 << (n - 1)) + (1 << m)) + n + m + 1); } } return dp[target]; } private: int dp[10001]; };