[LeetCode] 818. Race Car

本文介绍了一种利用动态规划解决赛车问题的方法,旨在找到达到指定位置所需的最短指令序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接: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.youkuaiyun.com/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.youkuaiyun.com/magicbean2/article/details/80333734

### 如何在 VSCode 中安装和配置 LeetCode 插件以及 Node.js 行环境 #### 安装 LeetCode 插件 在 VSCode 的扩展市场中搜索 `leetcode`,找到官方提供的插件并点击 **Install** 按钮进行安装[^1]。如果已经安装过该插件,则无需重复操作。 #### 下载与安装 Node.js 由于 LeetCode 插件依赖于 Node.js 环境,因此需要下载并安装 Node.js。访问官方网站 https://nodejs.org/en/ 并选择适合当前系统的版本(推荐使用 LTS 版本)。按照向导完成安装流程后,需确认 Node.js 是否成功安装到系统环境中[^2]。 可以通过命令行行以下代码来验证: ```bash node -v npm -v ``` 上述命令应返回对应的 Node.js 和 npm 的版本号。如果没有正常返回版本信息,则可能未正确配置环境变量。 #### 解决环境路径问题 即使完成了 Node.js 的安装,仍可能出现类似 “LeetCode extension needs Node.js installed in environment path” 或者 “command ‘leetcode.toggleLeetCodeCn’ not found” 的错误提示[^3]。这通常是因为 VSCode 未能识别全局的 Node.js 路径或者本地安装的 nvm 默认版本未被正确加载[^4]。 解决方法如下: 1. 手动指定 Node.js 可执行文件的位置 在 VSCode 设置界面中输入关键词 `leetcode`,定位至选项 **Node Path**,将其值设为实际的 Node.js 安装目录下的 `node.exe` 文件位置。例如:`C:\Program Files\nodejs\node.exe`。 2. 使用 NVM 用户管理工具调整默认版本 如果通过 nvm 工具切换了不同的 Node.js 版本,请确保设置了默认使用的版本号。可通过以下指令实现: ```bash nvm alias default <version> ``` 重新启动 VSCode 后测试功能键是否恢复正常工作状态。 --- #### 配置常用刷题语言 最后一步是在 VSCode 设置面板中的 LeetCode 插件部分定义个人习惯采用的主要编程语言作为默认提交方式之一。这样可以减少频繁修改编码风格的时间成本。 --- ### 总结 综上所述,要在 VSCode 上顺利启用 LeetCode 插件及其关联服务,除了基本插件本身外还需额外准备支持性的后台框架——即 Node.js 应用程序引擎;同时针对特定场景下产生的兼容性障碍采取针对性措施加以修正即可达成目标[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值