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 ...
随机推荐
- hdu1693 Eat the Trees 【插头dp】
题目链接 hdu1693 题解 插头\(dp\) 特点:范围小,网格图,连通性 轮廓线:已决策点和未决策点的分界线 插头:存在于网格之间,表示着网格建的信息,此题中表示两个网格间是否连边 状态表示:当 ...
- Zend Hash table 详解--转
原文地址:http://www.phppan.com/2009/12/zend-hashtable/ 在PHP的Zend引擎中,有一个数据结构非常重要,它无处不在,是PHP数据存储的核心,各种常量.变 ...
- android adb devices 后出现offline问题的解决
Android在使用adb 命令时有时会发现设备的状态是offline,我的神呀,这怎么让我们调试呢.必须解决这个问题. 解决方式如下: 以下方法请确保你的设备处于调试模式下. 方法一:执行 #adb ...
- 【分块,莫队】【P4396】【AHOI2013】作业
传送门 Description 此时己是凌晨两点,刚刚做了Codeforces的小A掏出了英语试卷.英语作业其实不算多,一个小时刚好可以做完.然后是一个小时可以做完的数学作业,接下来是分别都是一个小时 ...
- (一)SVM原理
前言 本文开始主要介绍一下SVM的分类原理以及SVM的数学导出和SVM在Python上的实现.借鉴了许多文章,会在后面一一指出,如果有什么不对的希望能指正. 一. SVM简介 首先看到SVM是在斯坦福 ...
- opencv2 直方图之calchist函数使用(转)
OpenCV提供了calcHist函数来计算图像直方图. 其中C++的函数原型如下:void calcHist(const Mat* arrays, int narrays, const int* c ...
- mybatis基础犯错总结
1.关于mybatis的文件一般都是其mapper文件出错: 首先关于输入参数parameterType出错: (1)基本数据类型:如果输入参数只有一个,其数据类型可以是基本数据类型,也可以是自己定的 ...
- libxml2在mingw下编译
1.配置MingW路径,在环境变量path中加入/mingw32/bin2.解压libxml,进入win32目录3.记事本打开configure.js,找到var compiler = "m ...
- Codechef Dynamic Trees and Queries
Home » Practice(Hard) » Dynamic Trees and Queries Problem Code: ANUDTQSubmit https://www.codechef.co ...
- 复制自身程序到windows目录和system32目录下
功能:复制自身到windows目录和system32目录下. 参考代码: #include <stdio.h> #include <windows.h> void CopySe ...