2023-05-14:你的赛车可以从位置 0 开始,并且速度为 +1 ,在一条无限长的数轴上行驶,

赛车也可以向负方向行驶,

赛车可以按照由加速指令 'A' 和倒车指令 'R' 组成的指令序列自动行驶。

当收到指令 'A' 时,赛车这样行驶:

position += speed,

speed *= 2。

当收到指令 'R' 时,赛车这样行驶:

如果速度为正数,那么speed = -1,

否则 speed = 1,

当前所处位置不变。

例如,在执行指令 "AAR" 后,赛车位置变化为 0 --> 1 --> 3 --> 3,

速度变化为 1 --> 2 --> 4 --> -1,

给你一个目标位置 target ,返回能到达目标位置的最短指令序列的长度。

输入:target = 3。

输出:2。

答案2023-05-14:

算法1 - Dijkstra 算法

1.初始化

1.1.设置变量 maxp,表示当前速度下能达到的最大位置,同时计算最大速度 maxs;

1.2.初始化一个优先队列(堆),保存状态 state{speed, cost, position},其中 speed 表示当前速度,cost 表示到达该状态所需的步数,position 表示当前位置;

1.3.根据最初的位置和速度创建初始状态,将其压入优先队列中。

2.Dijkstra 算法遍历状态空间

2.1.从优先队列中取出当前代价最小/速度绝对值最大的状态 state0;

2.2.若该状态满足目标条件,则返回其代价 cost;

2.3.否则,考虑在该状态基础上执行 A 或 R 操作后能够到达的状态:

2.3.1.若执行 A 操作,则新状态为 {speed+1, cost+1, position+(1<<(speed-1))},必须满足新位置不超过 maxp、未访问过;

2.3.2.若执行 R 操作,则新状态为 {speed>0?-1:1, cost+1, position},无需判断是否超过边界、未访问。

2.4.将所有可行的新状态加入优先队列,并继续进行 Dijkstra 遍历。

3.返回 -1,如果无法到达目标位置。

时间复杂度:O(T log T),其中 T 是目标位置 target。每个状态最多被扩展一次,因此总共扩展的状态数不会超过 O(T)。在优先队列中插入和弹出元素的时间复杂度为 O(log T),因此总时间复杂度为 O(T log T)。

空间复杂度:O(T log T)。需要开辟一个大小为 O(T log T) 的优先队列、两个大小为 O(T log T) 的二维数组 visitedPositive 和 visitedNegative,以及一个大小为 O(T) 的判断是否访问过的数组。

算法2 - 动态规划

1.初始化

1.1.创建长度为 target+1 的数组 dp,用于保存到达每个位置的最短步数;

1.2.调用 process(target, dp) 函数进行递归求解。

2.递归求解

2.1.若 dp[target] > 0,说明已经计算过到达该位置的最短步数,直接返回 dp[target];

2.2.计算当前速度下能够到达的最远位置 maxp 和最大速度 maxs;

2.3.如果目标位置就在当前速度达不到的位置之前,则必须先倒车,再加速到目标位置;

若目标位置恰好与当前速度所达到的最远位置相同,则无需倒车。

2.4.对于以上情况,分别计算:

2.4.1.倒车后可以到达的位置 beyond = speed-1-target;

2.4.2.从新的位置开始加速到目标位置,需要的最短步数为 process(beyond, dp),

在此基础上需要增加 1 次倒车操作和 1 次加速操作,因此总步数为 steps+1+process(beyond, dp)。

2.5.如果目标位置在当前速度达到的范围内,则直接加速即可。计算需要的最短步数,以及在此基础上还需要多少次加速操作(steps),

然后遍历所有加速操作的次数 back,计算倒车后可以到达的位置 lack 和需要的步数 steps+1+back+1+process(lack, dp),

取其中的最小值即为当前情况下的最短步数。

2.6.将结果保存到数组 dp 中,并返回。

3.返回 dp[target]。

时间复杂度:O(T log T)。虽然是递归求解,但是可以使用记忆化优化,避免重复计算。每个位置最多只会被计算一次,因此总时间复杂度为 O(T)。

空间复杂度:O(T)。需要创建一个大小为 O(T) 的数组 dp 保存中间结果。

go完整代码如下:

package main

import (
"container/heap"
"fmt"
) type state struct {
speed, cost, position int
} type priorityQueue []state func (pq priorityQueue) Len() int { return len(pq) }
func (pq priorityQueue) Less(i, j int) bool {
return pq[i].cost > pq[j].cost
}
func (pq priorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *priorityQueue) Push(x interface{}) {
item := x.(state)
*pq = append(*pq, item)
} func (pq *priorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
} func abs(x int) int {
if x < 0 {
return -x
}
return x
} func racecar1(target int) int {
maxp := 0
maxs := 1
for maxp <= target {
maxp += 1 << (maxs - 1)
maxs += 1
} heap0 := &priorityQueue{}
visitedPositive := make([][]bool, maxs+1)
visitedNegative := make([][]bool, maxs+1)
for i := range visitedPositive {
visitedPositive[i] = make([]bool, maxp+1)
visitedNegative[i] = make([]bool, maxp+1)
} heap.Push(heap0, state{
speed: 1,
cost: 0,
position: 0,
}) for heap0.Len() > 0 {
current := heap.Pop(heap0).(state)
speed := current.speed
cost := current.cost
position := current.position
if position == target {
return cost
}
if speed > 0 {
if visitedPositive[speed][position] {
continue
}
visitedPositive[speed][position] = true
add(
speed+1,
cost+1,
position+(1<<(speed-1)),
maxp,
heap0,
visitedPositive,
)
add(
-1,
cost+1,
position,
maxp,
heap0,
visitedNegative,
)
} else {
speed := -speed
if visitedNegative[speed][position] {
continue
}
visitedNegative[speed][position] = true
add(
-(speed + 1),
cost+1,
position-(1<<(speed-1)),
maxp,
heap0,
visitedNegative,
)
add(
1,
cost+1,
position,
maxp,
heap0,
visitedPositive,
)
}
}
return -1
} func add(
speed int,
cost int,
position int,
limit int,
heap0 *priorityQueue,
visited [][]bool,
) {
if position >= 0 && position <= limit && !visited[abs(speed)][position] {
heap.Push(heap0, state{
cost: cost,
speed: speed,
position: position,
})
}
} // 动态规划 + 数学
func racecar2(target int) int {
dp := make([]int, target+1)
return process(target, dp)
} func process(target int, dp []int) int {
if dp[target] > 0 {
return dp[target]
}
steps := 0
speed := 1
for speed <= target {
speed <<= 1
steps++
}
ans := 0
beyond := speed - 1 - target
if beyond == 0 {
ans = steps
} else {
ans = steps + 1 + process(beyond, dp)
steps--
speed >>= 1
lack := target - (speed - 1)
offset := 1
for back := 0; back < steps; back++ {
ans = min(ans, steps+1+back+1+process(lack, dp))
lack += offset
offset <<= 1
}
}
dp[target] = ans
return ans
} func min(a, b int) int {
if a < b {
return a
}
return b
} func main() {
target := 3
result1 := racecar1(target)
result2 := racecar2(target)
fmt.Println(result1)
fmt.Println(result2) target = 6
result1 = racecar1(target)
result2 = racecar2(target)
fmt.Println(result1)
fmt.Println(result2)
}

rust完整代码如下:

use std::cmp::Reverse;
use std::collections::BinaryHeap; fn racecar1(target: i32) -> i32 {
let mut maxp = 0;
let mut maxs = 1;
while maxp <= target {
maxp += 1 << (maxs - 1);
maxs += 1;
}
// 0 : 几倍速
// 1 : 花费了几步
// 2 : 当前位置
let mut heap = BinaryHeap::new();
let mut positive = vec![vec![false; (maxp + 1) as usize]; (maxs + 1) as usize];
let mut negative = vec![vec![false; (maxp + 1) as usize]; (maxs + 1) as usize];
heap.push((Reverse(0), Reverse(1), Reverse(0)));
while let Some((Reverse(cost), Reverse(speed), Reverse(position))) = heap.pop() {
if position == target {
return cost;
}
if speed > 0 {
if positive[speed as usize][position as usize] {
continue;
}
positive[speed as usize][position as usize] = true;
add(
speed + 1,
cost + 1,
position + (1 << (speed - 1)),
maxp,
&mut heap,
&positive,
);
add(-1, cost + 1, position, maxp, &mut heap, &negative);
} else {
let speed = -speed;
if negative[speed as usize][position as usize] {
continue;
}
negative[speed as usize][position as usize] = true;
add(
-(speed + 1),
cost + 1,
position - (1 << (speed - 1)),
maxp,
&mut heap,
&negative,
);
add(1, cost + 1, position, maxp, &mut heap, &positive);
}
}
-1
} fn add(
speed: i32,
cost: i32,
position: i32,
limit: i32,
heap: &mut BinaryHeap<(Reverse<i32>, Reverse<i32>, Reverse<i32>)>,
visited: &Vec<Vec<bool>>,
) {
if position >= 0 && position <= limit && !visited[speed.abs() as usize][position as usize] {
heap.push((Reverse(cost), Reverse(speed), Reverse(position)));
}
} fn racecar2(target: i32) -> i32 {
let mut dp = vec![0; (target + 1) as usize];
process(target, &mut dp)
} fn process(target: i32, dp: &mut Vec<i32>) -> i32 {
if dp[target as usize] > 0 {
return dp[target as usize];
}
let mut steps = 0;
let mut speed = 1;
while speed <= target {
speed <<= 1;
steps += 1;
}
let mut ans = 0;
let beyond = speed - 1 - target;
if beyond == 0 {
ans = steps;
} else {
ans = steps + 1 + process(beyond, dp);
steps -= 1;
speed >>= 1;
let mut lack = target - (speed - 1);
let mut offset = 1;
for back in 0..steps {
ans = ans.min(steps + 1 + back + 1 + process(lack, dp));
lack += offset;
offset <<= 1;
}
}
dp[target as usize] = ans;
ans
} fn main() {
let target = 3;
let result1 = racecar1(target);
println!("{}", result1); let result2 = racecar2(target);
println!("{}", result2); let target = 6;
let result1 = racecar1(target);
println!("{}", result1); let result2 = racecar2(target);
println!("{}", result2);
}

c语言第二种方法代码如下:

#include <stdio.h>
#include <stdlib.h> int racecar2_helper(int target, int* dp) {
if (dp[target] > 0) {
return dp[target];
}
int steps = 0;
int speed = 1;
while (speed <= target) {
speed <<= 1;
steps++;
}
int ans = 0;
int beyond = speed - 1 - target;
if (beyond == 0) {
ans = steps;
}
else {
ans = steps + 1 + racecar2_helper(beyond, dp);
steps--;
speed >>= 1;
int lack = target - (speed - 1);
int offset = 1;
for (int back = 0; back < steps; back++) {
ans = (ans < steps + 1 + back + 1 + racecar2_helper(lack, dp)) ?
ans : steps + 1 + back + 1 + racecar2_helper(lack, dp);
lack += offset;
offset <<= 1;
}
}
dp[target] = ans;
return ans;
} int racecar2(int target) {
int* dp = (int*)calloc((target + 1), sizeof(int));
int result = racecar2_helper(target, dp);
free(dp);
return result;
} int main() {
int target = 3;
printf("racecar2: %d", racecar2(target));
target = 6;
printf("racecar2: %d", racecar2(target));
return 0;
}

c++第二种方法代码如下:

#include <iostream>
#include <vector> using namespace std; int racecar2_helper(int target, vector<int>& dp) {
if (dp[target] > 0) {
return dp[target];
}
int steps = 0;
int speed = 1;
while (speed <= target) {
speed <<= 1;
steps++;
}
int ans = 0;
int beyond = speed - 1 - target;
if (beyond == 0) {
ans = steps;
}
else {
ans = steps + 1 + racecar2_helper(beyond, dp);
steps--;
speed >>= 1;
int lack = target - (speed - 1);
int offset = 1;
for (int back = 0; back < steps; back++) {
ans = min(ans, steps + 1 + back + 1 + racecar2_helper(lack, dp));
lack += offset;
offset <<= 1;
}
}
dp[target] = ans;
return ans;
} int racecar2(int target) {
vector<int> dp(target + 1, 0);
return racecar2_helper(target, dp);
} int main() {
int target = 3;
cout << "racecar2: " << racecar2(target) << endl;
target = 6;
cout << "racecar2: " << racecar2(target) << endl;
return 0;
}

2023-05-14:你的赛车可以从位置 0 开始,并且速度为 +1 ,在一条无限长的数轴上行驶, 赛车也可以向负方向行驶, 赛车可以按照由加速指令 ‘A‘ 和倒车指令 ‘R‘ 组成的指令序列自动行驶的更多相关文章

  1. 自己动手写CPU之第五阶段(3)——MIPS指令集中的逻辑、移位与空指令

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第17篇.我尽量每周四篇 5.4 逻辑.移位操作与空指令说明 MIPS32指令集架构中定义的逻辑操作指令有8条:and.and ...

  2. ARM指令集中经常使用的存储和载入指令

    ARM微处理器支持载入/存储指令用于在寄存器和存储器之间传送数据,载入指令用于将存储器中的数据传送到寄存器,存储指令则完毕相反的操作.经常使用的载入存储指令例如以下: -  LDR     字数据载入 ...

  3. java selenium启动火狐浏览器报错:Cannot find firefox binary in PATH. Make sure firefox is installed. OS appears to be: VISTA Build info: version: '3.8.1', revision: '6e95a6684b', time: '2017-12-01T19:05:14.666Z

    Cannot find firefox binary in PATH. Make sure firefox is installed. OS appears to be: VISTA Build in ...

  4. 斗篷指令、属性指令、表单指令、条件指令、循环指令、js的Array操作、前台数据库、

    ```python"""1)指令 属性指令:v-bind 表达指令:v-model 条件指令:v-show v-if 循环指令:v-for 斗篷指令:v-cloak 2) ...

  5. 2021.05.14 tarjan

    2021.05.14 tarjan 标准版tarjan 这里使用数组来模拟栈 void tarjan(int x){ ++ind; dfn[x]=low[x]=ind; stacki[++top]=x ...

  6. Python:安装opencv出现错误Could not find a version that satisfies the requirement numpy==1.13.3 (from versions: 1.14.5, 1.14.6, 1.15.0rc2, 1.15.0, 1.15.1, 1.15.2, 1.15.3, 1.15.4, 1.16.0rc1, 1.16.0rc2,

    安装opencv的时候,出现numpy的版本不匹配,卸载了不匹配的版本,重新安装却是一点用都没有,后面尝试了一下这里的提示pip更新,居然安装成功了,看来pip的版本过低真是误事啊. 报错是: Cou ...

  7. ngx_lua_API 指令详解(四)ngx.exec指令

    https://github.com/openresty/lua-nginx-module#ngxexec 参照:http://blog.csdn.net/weiyuefei/article/deta ...

  8. INPUT和CONSTRUCT指令——范例报表查询,作用让用户输入数据,自动生成SQL的WHERE条件,带开窗查询

    INPUT指令 说明:1. 当程序执行到INPUT指令时,会将控制权交给用户,让用户输入数据.2. 用户输入完字段的数据,会将数据回传给程序中的变量接收.3. 只要执行到INPUT的指令,程序会将每个 ...

  9. 寻找大学目标及行动步骤——记ITAEM团队第二期宣讲会(2014.05.14)

    ·昨晚8:00-9:40.在 钟海楼03029 ,进行了ITAEM团队第二期宣讲会(第一期见第一期宣讲会总结).来參加的主要是大一学生.以信院为主.也有法学院.文学院的同学. 在宣讲会中,大家都比較积 ...

  10. OpenSpiel 随笔 05.14

    ------------恢复内容开始------------ 这两天年总算把自己的游戏写完了,也通过了所有的测试. 我将自己的代码上传到了我的github上, 地址是 https://github.c ...

随机推荐

  1. git 设置记住密码和清除密码

    git 设置记住密码和清除密码   1. 永久记住密码 该命令会记住密码,执行一次 git pull 或 git push 等需要输入密码的命令,输入一次密码, 之后就都不必再输入了 git conf ...

  2. the solution of Mining Your Own Business

    the description of problem (我看的是 PDF 里面的原题所以这里描述会和题目不一样,但是大意一致) 给定一个未必连通的无向图,问最少在几个点设置出口,可以保证任意一个点坍塌 ...

  3. OpenSSL 生成 RootCA (根证书)并自签署证书(支持 IP 地址)

    背景 某机房内部访问需要配置 HTTPS,网上找的一些证书教程都不是特别好,有些直接生成证书,没有根 CA 的证书导致信任不了 Ubuntu 机器,有些教程只有域名生成,没有 IP 生成,有些甚至报错 ...

  4. SQL 语句 增删改查、边学习边增加中..... 这一部分为select

    SQL语句按照最大的类别分为 1.增加 insert 2.删除 delete  https://www.cnblogs.com/kuangmeng/p/17756654.html 3.修改update ...

  5. macbook通过虚拟机连接远程linux

    之前操作远程虚拟机,都是用window系统,现第一次用linux命令操作一下linux系统. 苹果启动term 输入ssh root@192.168.3.154连接linux,ssh 用户名@服务器i ...

  6. 【本博客所有关于git文章迭代汇总】git操作(暂存,回退,绑定远程等),看这一篇就够了

    1.git常用操作 git 小白操作,无非是clone,然后拉取,提交分支,第一次clone的时候,关联远程分支可能会遇到问题,可以看第四条git关联远程分支 # 在当前目录新建一个Git代码库 $ ...

  7. JavsScript对密码进行Base64加密和Base64解密

    const password = "hello"; // 进行Base64加密 let pwd64 = window.btoa(password); console.log(pwd ...

  8. 如何保证Spring Boot接口安全的呢?

    在保证Spring Boot接口安全时,我们需要关注的主要方面包括:认证(Authentication).授权(Authorization).数据安全性(Data Security).以及防止常见的W ...

  9. C/C++ 常用加密与解密算法

    计算机安全和数据隐私是现代应用程序设计中至关重要的方面.为了确保数据的机密性和完整性,常常需要使用加密和解密算法.C++是一种广泛使用的编程语言,提供了许多加密和解密算法的实现.本文将介绍一些在C++ ...

  10. Linux笔记01: Linux简介

    1.1 操作系统 计算机系统是由硬件子系统(处理器.内存.硬盘.键盘.鼠标.显示屏等)和软件子系统(如Windows操作系统.Office办公软件等)组成. 操作系统(Operating System ...