题目地址:
https://leetcode.com/problems/race-car/
有一辆车处在位置
0
0
0,初始速度为
1
1
1,有一个目标位置
t
t
t。如果其当前速度为
v
v
v,位置为
x
x
x,每一步其可以采取如下操作:
1、其可以走到
x
+
v
x+v
x+v的位置,并且
v
v
v变为
2
v
2v
2v;
2、其可以将
v
v
v变为
−
v
∣
v
∣
-\frac{v}{|v|}
−∣v∣v(速度变为负数的意思是方向反向)。
问其至少多少步能到达
t
t
t,其中
t
≥
1
t\ge 1
t≥1。
由于速度一定是 2 2 2的幂次,我们可以将 ( x , k , d ) (x,k,d) (x,k,d)做成一个状态, x x x是位置, k k k是速度的 2 2 2的指数, d d d是方向,正向为 1 1 1负向为 0 0 0。这样初始状态就是 ( 0 , 0 , 1 ) (0,0,1) (0,0,1)。那么问题就转为从 ( 0 , 0 , 1 ) (0,0,1) (0,0,1)走到 ( t , . , . ) (t,.,.) (t,.,.)至少要多少步,每一步可以走到 ( x + 2 k , k + 1 , d ) (x+2^k, k+1, d) (x+2k,k+1,d)或者 ( x , 0 , d ∧ 1 ) (x, 0, d\wedge 1) (x,0,d∧1),可以用BFS来解决。我们可以将连续的同方向的加速分隔出来,那么每次走的距离是 1 + 2 1 + . . . + 2 c − 1 = 2 c − 1 1+2^1+...+2^{c-1}=2^c-1 1+21+...+2c−1=2c−1,而这样的数的二进制表示即为连续的若干个 1 1 1。问题即是要用若干个形如那样的整数的加减凑出 t t t。如果走到 > 2 t >2t >2t的位置是不划算的,同样如果走到 < 0 <0 <0的位置也不划算。代码如下:
class Solution {
public:
struct Node {
int x, k, d;
bool operator==(const Node& n) const {
return x == n.x && k == n.k && d == n.d;
}
};
int racecar(int target) {
queue<Node> q;
auto nhash = [](const Node& n) {
auto h = hash<int>();
return h(n.x) ^ h(n.k) ^ h(n.d);
};
unordered_set<Node, decltype(nhash)> vis(0, nhash);
q.push({0, 0, 1});
vis.insert({0, 0, 1});
int res = 0;
while (q.size()) {
res++;
for (int i = q.size(); i; i--) {
auto t = q.front(); q.pop();
int x = t.x + (1 << t.k) * (t.d * 2 - 1);
if (0 <= x && x <= target * 2) {
if (x == target) return res;
if (!vis.count({x, t.k + 1, t.d})) {
q.push({x, t.k + 1, t.d});
vis.insert({x, t.k + 1, t.d});
}
}
x = t.x;
int d = t.d ^ 1;
if (!vis.count({x, 0, d})) {
q.push({x, 0, d});
vis.insert({x, 0, d});
}
}
}
return -1;
}
};
时空复杂度 O ( t log t ) O(t\log t) O(tlogt)。