当青训营遇上码上掘金
这次我选择的是 主题三:寻友之旅
题目
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
- 步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
- 公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K 输出: 小青到小码家所需的最短时间 (单位: 分)
解题思路
思考
有两种情况,第一种即 N > K 时 这种情况下由于公交车只能向前走, 所以肯定只能步行,一次走一格。
所以需要考虑 N < K的情况。
可能遇到的情况:
- 公交直达
N = 10, K = 20则此时10 -> 公交 10*2 -> 20 - 步行 -> 公交
N=6, k = 20则此时6 -> 步行 5 -> 公交 10 -> 公交 20 - 公交 -> 步行 -> 公交
N = 6, K = 26则此时6 -> 公交 12 -> 步行 13 -> 公交 26 - ...
由于N,K 可能出现的情况很多, 考虑问题时 很容易忽略其他解而得到错误的答案,我们可能考虑使用广度优先算法1, 来解决这类问题.
求解
可以使用广度优先搜索(BFS)来解决这个问题。将小青的当前位置作为起点,小码的位置作为终点,然后从起点开始搜索。
具体实现可以使用一个队列来存储当前所有可以到达的位置,每次从队列中取出第一个位置,然后把它能到达的位置加入队列。如这里我使用一个dist数组来记录每个节点到达起始节点n的距离。使用一个visited数组来记录每个节点是否被遍历过。
每次从队列中取出第一个节点,将其周围未被遍历过的节点加入队列并将其标记为已遍历。如果当前节点是K则代表遍历结束,输出此时的 dist['cur'] 即所需结果。
代码
package main
import "fmt"
const (
max = 100005
)
var (
n, k int
dist [max]int
visited [max]bool
)
func main() {
fmt.Println("input n, k:")
fmt.Scan(&n, &k)
// fittler
if n < 0 || k > 100000 {
fmt.Println("invalid input")
return
}
queue := []int{n}
dist[n] = 0
visited[n] = true
for len(queue) > 0 {
// 将起始点压入队列
cur := queue[0]
queue = queue[1:]
if cur == k {
fmt.Println(dist[cur])
return
}
// 步行向后
if cur-1 >= 0 && !visited[cur-1] {
visited[cur-1] = true
dist[cur-1] = dist[cur] + 1
queue = append(queue, cur-1)
}
// 步行向前
if cur+1 < max && !visited[cur+1] {
visited[cur+1] = true
dist[cur+1] = dist[cur] + 1
queue = append(queue, cur+1)
}
// bus
if cur*2 < max && !visited[cur*2] {
visited[cur*2] = true
dist[cur*2] = dist[cur] + 1
queue = append(queue, cur*2)
}
}
}
Footnotes
-
广度优先算法(BFS) 是一种图论算法,它是从起点开始,沿着宽度优先遍历图中的所有节点。它使用一个队列来存储待遍历的节点,每次取出队列中的第一个节点并将其相邻节点加入队列。广搜算法可用于求最短路径、最小生成树、拓扑排序等问题。 ↩