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 ...
随机推荐
- 洛谷 P3157 [CQOI2011]动态逆序对 解题报告
P3157 [CQOI2011]动态逆序对 题目描述 对于序列\(A\),它的逆序对数定义为满足\(i<j\),且\(A_i>A_j\)的数对\((i,j)\)的个数.给\(1\)到\(n ...
- HDU.1495 非常可乐 (BFS)
题意分析 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多 ...
- 电子商务(电销)平台中系统设置模块(SysSetting)数据库设计明细
以下是自己在电子商务系统设计中的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 邮件服务器 (sys_smtp_server)|-- 自动编号|-- SMTP服务器地址 (host ...
- 「Python-Django」Django中使用数据库的 9 个小技巧
Django 中使用数据库的 9 个小技巧. 1. 过滤器聚合 在 Django 2.0 之前,如果你想得到“用户总数”.“活跃用户总数”等信息时,你不得不使用条件表达式. Django 2.0 中, ...
- CF760 D Travel Card 简单DP
link 题意:乘车,有3种票 1.20块坐1站 2.坐90分钟,50块 3.坐1440分钟,120块 现给出到达每个站的时间,问最优策略 思路: 简单DP,限定条件的3个转移方向,取最小的那个就行了 ...
- Spring Cacheable 注解不缓存null值
用Cacheable注解时,发现空值,也会被缓存下来.如果我们期望空值不被缓存,可以做如下设置: @Cacheable(key = "#id", unless="#res ...
- Isomorphic JavaScript: The Future of Web Apps(译)
Isomorp ...
- [linux]安装code::blocks
1.安装基本编译环境 $sudo apt-get install build-essential $sudo apt-get install gdb 2.安装codeblock $sudo apt-g ...
- 【Linux 命令】fping ping 包间隔时间详解
服务器间检查会用到fping的命令,期间遇到了一个问题,需要将ping包间的间隔时间设置为100毫秒,查看fping -h看下,找到了-i和-p两个参数: 看到这两个参数,我当时的表情是这样的: 看不 ...
- 【译】Attacking XML with XML External Entity Injection (XXE)
原文链接:Attacking XML with XML External Entity Injection (XXE) XXE:使用XML外部实体注入攻击XML 在XML中,有一种注入外部文件的方式. ...