[LeetCode] 818. Race Car

原题链接:https://leetcode.com/problems/race-car/

1.题目介绍

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.

1 <= target <= 10000.

你的赛车起始停留在位置 0,速度为 +1,正行驶在一个无限长的数轴上。(车也可以向负数方向行驶。)

你的车会根据一系列由 A(加速)和 R(倒车)组成的指令进行自动驾驶 。

当车得到指令 “A” 时, 将会做出以下操作: position += speed, speed *= 2。

当车得到指令 “R” 时, 将会做出以下操作:如果当前速度是正数,则将车速调整为 speed = -1 ;否则将车速调整为 speed = 1。 (当前所处位置不变。)

例如,当得到一系列指令 “AAR” 后, 你的车将会走过位置 0->1->3->3,并且速度变化为 1->2->4->-1。

现在给定一个目标位置,请给出能够到达目标位置的最短指令列表的长度。
1 <= target <= 10000.

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.

2. 解题思路-动态规划

对于求极值的题目,主要有一下方法:

  1. 广度优先搜索BFS
  2. 深度优先搜索DFS(带剪枝)
  3. 贪心算法
  4. 动态规划(带记忆数组的递归也可以算动态规划)

比如这个题就用动态规划可以解决。
注:本文采用的动态规划解法参考了 https://blog.csdn.net/magicbean2/article/details/80333734 的部分介绍。

2.1 构造dp数组

采用动态规划的思路,定义 dp [ target ] 表示行驶长度为 target 的距离所需要的最小指示个数。

2.2 寻找递推关系式

1 . 如果 target 恰好是 2n - 1,那么到达该位置只需要 n 个 A 就可以了。也就是说前面一路加速,直到到达target为止。

2 . 如果不满足上面的1,那么最优解就有2种可能:

(a)一路加速,冲过target之后再调头后退,逐渐反向接近target。
此时我们已经走了n+1步(n个 A 和1个 R ),并且和target的距离已经减少到了2n - 1 - target,于是问题转化成了: 求行驶长度为 2n - 1 - target 的距离所需要的最小指示个数。也就是 dp[ 2n - 1 - target ]

(b)在冲过target之前,先向后退一点,然后再向target接近。
在冲过target的前一步停下来,调头向后走m个"A" ,走完m个"A"后,再次调头,此时距离 targe 的距离是target - [2(n-1)-1]+(2m-1) 。然后再使用递归函数求去往距离为 target - [2(n-1)-1]+(2m-1) 位置的最小指令数。其中的m可以是任何满足2m - 1 < 2(n-1)-1 的数,不一定是哪个,所以要从m = 0 开始遍历。
(这种情况的典型例子就是 target = 5 的时候,最小的指令数为7,指令为"AAR-AR-AA".)

最优解就是 (a) 和(b)之间的最小值。

我们使用带记忆数组的递归来进行动态规划。如果dp数组中已经有了对应的值,就直接使用;如果没有,使用递归函数计算需要的值并存储在dp数组里。

实现代码

class Solution {
    int [] dp;
	public int racecar(int target) {
		dp  = new int [target+1];
		return recursion(target);
	}
	
	public int recursion(int target) {
		
        if(dp[target] != 0) {
        	return dp[target];
        }
        
        //如果位置恰好是2^n-1,那么到达该位置只需要n个A就可以了。
        double dn = Math.log(target )/Math.log(2);
        int n = (int) Math.floor(dn) +1;
        if ( (1<<n)-1 == target) {
            dp[target] = n;
        }
        
        //对于位置不是2^n-1 的情况,就需要比较两种可能性,取其中较小的一个。
        else {
        	//第一种
        	//冲过target之后再调头后退,和target的距离已经减少到了2^n - 1 - target
        	int a = recursion( (1 << n) - 1 - target) + n + 1;
   
        	//第二种
        	//在冲过target的前一步停下来,调头向后走m个"A"
        	//走完m个"A"后,再次调头,此时距离targe的距离是target-[2^(n-1)-1]+(2^m-1)
        	//然后再使用递归函数求去往距离为 target-[2^(n-1)-1]+(2^m-1) 位置的最小指令数
        	int b = Integer.MAX_VALUE;
        	for (int m = 0; m < n - 1; m++) {
        		int dist = target-( (1<<(n-1)) - 1 ) + ( (1<<m) - 1 );
                b = Math.min( b , recursion(dist) + (n-1) + 1 + m + 1);
                //即:先走(n-1)个A,然后调头1个R,再走m个A,再调头1个R,之后继续向target接近。
        	}
        	
        	//取两者最小的
        	dp[target] = Math.min(a, b);
        }
        return dp[target];
    }
}

3. 参考资料

https://blog.csdn.net/magicbean2/article/details/80333734

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值