当青训营遇上码上掘金 之 "寻友之旅"
寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久
输入
两个整数 N 和 K
输出
小青到小码家所需的最短时间(以分钟为单位)
解题思路
找关键点
- 横向单维搜索
- 单点搜索次数
- 公交方式只能往前走
- 到达地点K的最短时间
确定算法
- 广度优先搜索(BFS)
思考过程
-
由于BFS暴搜的原因,遍历会出现同一个点重复遍历的情况,故我们要用一个映射记录一下每个点是否已经走过
-
搜索过程中,其实我们搜索的对象是队列,我这里是考虑在队列中存放我们采用步行或者是公交得出的下一个点的位置
-
我们每次得出下一个点的位置的时候,我们就需要及时更新一下到达下一个点的位置时总共需要的时间开销,形成一个时间前缀和的映射
-
当到达的下一个点就是终点,即小青到了小码家,这时候我们就可以将搜索结果返回,因为此时是第一次到达指定的点位,在BFS中,首次到达特定点位即路径最短,亦时间最短
特殊情况
- 当且仅当小码的位置位于小青后方时(N>K),由于公交车不能往后走,因此小青只能选择步行的方式来去到达小码的位置
func solve(n, k int) int {
// 小码的位置位于小青后方
if n >= k {
return n - k
}
// 否则开始搜索
return bfs(n, k)
}
核心代码实现
func bfs(n, k int) int {
make_init(n, k)
// 记录起点
q = append(q, n)
for len(q) != 0 {
// 取出起点
cur = q[0]
q = q[1:]
// 选择方案
for i := 0; i < 3; i++ {
// 步行往后走
if i == 0 {
next = cur - 1
}
// 步行往前走
if i == 1 {
next = cur + 1
}
// 公交
if i == 2 {
next = cur * 2
}
// 边界判断
if next < 0 || next > k {
continue
}
// 方案入队
if vis[next] == 0 {
vis[next] = 1
q = append(q, next)
cost[next] = cost[cur] + 1
}
// 是否终点
if next == k {
return cost[next]
}
}
}
}