当青训营遇上码上掘金
题目:
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
解题思路:
-
我们需要使用一个队列wait来存储待处理的结点,由于采用的是广度优先搜索的思路,因此每当搜索到一个结点时,都会将其入队,然后再将元素(点)依次出队来搜索该元素(点)可以到达的位置,在搜索过程中,会更新dist和flag数组中的相关值。
-
为了解决同一个点重复遍历的情况,我们需要flag数组,数组中每个元素代表当前序号的结点是否已经过队列处理。初始化时,将每个元素赋值为0,表示尚未处理该结点;等结点进入队列时,将值更新为1。
-
需要考虑两种情况,
N > K时,公交车只能向前走, 所以只能步行,消费为1。考虑N < K的情况,则采用bfs算法遍历。
算法设置:
- 先定义一个dist数组,数组中每个元素存储小青到该结点的最短时间,初始化为最大值
- 再定义一个flag数组,记录每个位置是否被访问过,初始化为0,访问过置1
- 定义一个wait数组,记录所有小青可以访问的位置,初始化为0
- 将小青的位置加入到wait数组中,并将其flag置为1,dist置为0
- 从wait数组中弹出一个元素(位置点),判断其可以访问的所有位置,并将其加入到wait数组中,并将其flag置为1
- 计算小青从该位置到达每个可以访问的位置的距离,并记录最小值到dist数组中
- 当wait数组为空时,结束循环
- 重复步骤5-7,直到wait数组为空,或wait数组弹出元素为目的地K
- 取出dist数组中的最小值,即为小青从起始位置到目的地的最小距离
代码:
package main
import (
"fmt"
"math"
)
func main() {
var N, K int
fmt.Printf("请输入N K,以空格隔开\r\n")
fmt.Scan(&N, &K)
fmt.Println("最短时间为:", calculate(N, K), "分钟")
}
func calculate(N, K int) int {
if N >= K {
return N - K //只能掉头步行,直接返回n-k
} else {
dist := make([]int, 100001)//每个点的距离
for i := 0; i < 100001; i++ {
dist[i] = math.MaxInt32
}
dist[N] = 0
flag := make([]int, 100001) //标志 元素是否已计算
flag[N] = 1
wait := make([]int, 0) //等待计算的元素
wait = append(wait, N)
for len(wait) != 0 {
num := wait[0] //取出队列首来计算
wait = wait[1:] //队首弹出
if num+1 < 100001 {
if dist[num+1] > dist[num]+1 {
dist[num+1] = dist[num] + 1
}
if flag[num+1] == 0 {
wait = append(wait, num+1)
flag[num+1] = 1
}
}
if num-1 >= 0 {
if dist[num-1] > dist[num]+1 {
dist[num-1] = dist[num] + 1
}
if flag[num-1] == 0 {
wait = append(wait, num-1)
flag[num-1] = 1
}
}
if num*2 < 100001 {
if dist[num*2] > dist[num]+1 {
dist[num*2] = dist[num] + 1
}
if flag[num*2] == 0 {
wait = append(wait, num*2)
flag[num*2] = 1
}
}
}
return dist[K]
}
}