LeetCode818. Race Car
https://leetcode.com/problems/race-car/description/
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.1 <= target <= 10000.
思路
想到了用BFS来遍历所有可能的组合,但是存储空间超时,需要剔除掉重复计算的部分。还有一种解法则是万能的dp。
BFS
主要优化是两个,一是记录每层遍历中的position和speed,这个构成一个状态,如果已经访问过则不加入队列中,还有一个是 0 < nxt[0] && nxt[0] < (target << 1)。这个不是很明白,网友给的解释如下:
There are cases when it is desirable to go past the target and then come back. For example, target = 6, we could go 0 --> 1 --> 3 --> 7 --> 7 --> 6, which takes 5 instructions (AAARA). If you reverse before passing the target, it takes more than 5 instructions to get to the target. That said, we don't want to go too far past the target. The rule of thumb (I have not proved it rigorously though) is that we stay within some limit from the target, and this limit is set by the initial distance from the target, which is target. So from the point of view of the target, we want the car to stay in the range [0, 2 * target]. This is the primary optimization for the BFS solution.
public int racecar(int target) {
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[] {0, 1}); // starts from position 0 with speed 1
Set<String> visited = new HashSet<>();
visited.add(0 + " " + 1);
for (int level = 0; !queue.isEmpty(); level++) {
for(int k = queue.size(); k > 0; k--) {
int[] cur = queue.poll(); // cur[0] is position; cur[1] is speed
if (cur[0] == target) {
return level;
}
int[] nxt = new int[] {cur[0] + cur[1], cur[1] << 1}; // accelerate instruction
String key = (nxt[0] + " " + nxt[1]);
if (!visited.contains(key) && 0 < nxt[0] && nxt[0] < (target << 1)) {
queue.offer(nxt);
visited.add(key);
}
nxt = new int[] {cur[0], cur[1] > 0 ? -1 : 1}; // reverse instruction
key = (nxt[0] + " " + nxt[1]);
if (!visited.contains(key) && 0 < nxt[0] && nxt[0] < (target << 1)) {
queue.offer(nxt);
visited.add(key);
}
}
}
return -1;
}
DP
dp的难点在于如何建立相应的dp模型,即能准确描述问题,又能方便找到递推公式。
参考:https://blog.csdn.net/magicbean2/article/details/80333734
采用动态规划的思路,定义dp[target]表示行驶长度为target的距离所需要的最小指示个数。看了半天dp解析,还是看不明白在说什么,最终找到了这个比较容易的解释:
https://leetcode.com/problems/race-car/discuss/123834/C++JavaPython-DP-solution
首先对于target,我们肯定能找到一个正整数 n 使得2 ^ (n - 1) <= target < 2 ^ n,即n是target的二进制形式的数字长度,比如对于4,那么其二进制是100,那么n=3。我们有如下连个策略:
1. Go pass our target , stop and turn back
We take n instructions of A.1 + 2 + 4 + ... + 2 ^ (n-1) = 2 ^ n - 1
Then we turn back by one R instruction.
In the end, we get closer by n + 1 instructions.
2. Go as far as possible before pass target, stop and turn back
We take n - 1 instruction of A and one R.
Then we take m instructions of A, where m < n
int[] dp = new int[10001];
public int racecar(int t) {
if (dp[t] > 0) return dp[t]; // 如果dp[t]已计算过则直接返回
int n = (int)(Math.log(t) / Math.log(2)) + 1;
if (1 << n == t + 1) dp[t] = n; // 1<<n-1=t 表示一路狂奔A就能到达target
else {
dp[t] = racecar((1 << n) - 1 - t) + n + 1; // 狂奔A到第一次冲过target,然后R,然后剩下的距离作为子问题递归求解
for (int m = 0; m < n - 1; ++m)
dp[t] = Math.min(dp[t], racecar(t - (1 << (n - 1)) + (1 << m)) + n + m + 1); // 标记1
}
return dp[t];
}
个人理解标记1处的计算是这样的,首先是 n-1 个A到达 2 ^ (n - 1) <= target < 2 ^ n 中前面的2 ^ (n - 1)位置,然后R,停在当前位置。然后注意现在是反向的,也就是向左,那么我们可以通过 0<=m<n-1 来遍历递归计算所有的可能,比如说m=0,那么
dp[t] = Math.min(dp[t], racecar(t - (1 << (n - 1)) + (1 << m)) + n + m + 1);
就变成了
Math.min(dp[t], racecar(t - (1 << (n - 1))) + n + 1)
其中 racecar(t - (1 << (n - 1))) 求解的是距离为 t - (1 << (n - 1)) 时的最优解,是个子问题。当我们走到2 ^ (n - 1)位置时,这样的子问题有很多个,遍历递归求这些子问题。
它这里遍历是逐步扩展左边来计算的,即会在2 ^ (n - 1)位置位置往左走m个A,其中m<n-1。
这里加上 n+1 是因为走了 n-1 个 A ,然后是1个 R。 往回走m个A时,还要一次R将方向置回。
只有在R之后才能作为相同性质的子问题处理,因为会重置speed为1,只不过这里要注意方向性。
LeetCode818. Race Car的更多相关文章
- [Swift]LeetCode818. 赛车 | Race Car
Your car starts at position 0 and speed +1 on an infinite number line. (Your car can go into negati ...
- Promise.race
[Promise.race] 返回最先完成的promise var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 5 ...
- golang中的race检测
golang中的race检测 由于golang中的go是非常方便的,加上函数又非常容易隐藏go. 所以很多时候,当我们写出一个程序的时候,我们并不知道这个程序在并发情况下会不会出现什么问题. 所以在本 ...
- 【BZOJ-2599】Race 点分治
2599: [IOI2011]Race Time Limit: 70 Sec Memory Limit: 128 MBSubmit: 2590 Solved: 769[Submit][Status ...
- hdu 4123 Bob’s Race 树的直径+rmq+尺取
Bob’s Race Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Probl ...
- Codeforces Round #131 (Div. 2) E. Relay Race dp
题目链接: http://codeforces.com/problemset/problem/214/E Relay Race time limit per test4 secondsmemory l ...
- 【多线程同步案例】Race Condition引起的性能问题
Race Condition(也叫做资源竞争),是多线程编程中比较头疼的问题.特别是Java多线程模型当中,经常会因为多个线程同时访问相同的共享数据,而造成数据的不一致性.为了解决这个问题,通常来说需 ...
- Codeforces Round #328 (Div. 2) C. The Big Race 数学.lcm
C. The Big Race Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/592/probl ...
- HDU 4123 Bob’s Race 树的直径 RMQ
Bob’s Race Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=41 ...
随机推荐
- 测试开发面试的Linux面试题总结之二:常用命令
(1)Linux的文件系统目录配置要遵循FHS规范,规范定义的两级目录规范如下: /home 每个账号在该目录下都有一个文件夹,进行数据的管理 /usr 有点像windo ...
- (转)Xsl 的Webshell(aspx)版本
关于使用xsl的webshell以前已经有人发过了,比如aspx的一个webshell如下: <%@ Page Language="C#" Debug="true& ...
- max os x lighttpd + php + mysql 部署
手贱,升级了max os x 到Yosemite,系统默认装了nginx,php,开机会自动启动!1 开机启动脚本默认在下面位置: /Library/LaunchDaemons/com.root.ng ...
- python基础----迭代器、生成器、协程函数及应用(面向过程实例)
一.什么是迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代 ...
- Codeforces Round #298 (Div. 2)A B C D
A. Exam time limit per test 1 second memory limit per test 256 megabytes input standard input output ...
- windows 安装 apache 服务以及添加 php 解析
apache 官方并没有提供 windows 的安装包,但是它们官网给出了第三方的链接,我们可以在那些第三方网站上找到适用于 windows 的二进制包. 我们点进去下载一个 64 位的, 下载完之后 ...
- socket编程学习step2
引言:主机之间如何相互交互呢?网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口“可以唯一标识主机中的应用进程.这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的 ...
- sql命令创建数据库
mysql create database 指定utf-8编码 CREATE DATABASE IF NOT EXISTS thinkphp DEFAULT CHARSET utf8 COLLATE ...
- ASP.NET Core的身份认证框架IdentityServer4--(5)自定义用户登录(通过接口登录,无UI版本)
官网接口详解文档地址:文档地址 (PS:可通过接口名称搜索相应接口信息.) 源码地址:https://github.com/YANGKANG01/IdentityServer4-IdentityAut ...
- LightOJ 1070 - Algebraic Problem 推导+矩阵快速幂
http://www.lightoj.com/volume_showproblem.php?problem=1070 思路:\({(a+b)}^n =(a+b){(a+b)}^{n-1} \) \(( ...