当青训营遇上码上掘金 | 主题 3:寻友之旅

101 阅读2分钟

当青训营遇上码上掘金 | 主题 3:寻友之旅

青训营笔记.png

当青训营遇上码上掘金 | 主题 3:寻友之旅

一、题目描述

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

测试数据:

3 5
2
10 1000
11
99 99999
15

二、解题思路

这道题目我们可以使用广度优先搜索BFS算法解决,具体做法如下:
首先我们从小青所在的地点出发,依次搜索小青可以到达的所有位置,并不断的加入待访问队列中,直到我们找到小码所在的位置。

具体实现:

  • 我们定义状态为:某个具体的位置
  • 则起始状态为小青所在的位置,终止状态为小码所在的位置。
  • 每次转移有两种方式:分别是步行和公交
  • 对于步行,我们将小青当前所在的位置+1或-1,如果在范围内,并且没有转移过,那就转移过去。
  • 对于公交,我们首先判断是否超过最远距离,然后按照步行的方式转移即可。
  • 然后我们维护一个dist数组,表示从小青的初始位置出发,到达位置i时需要用的最短时间,那么到达小码的位置的最短时间就是dist[K]
  • 对于最远的距离,明显是不会大于K+1的,考虑当N < K的时候,小青有两种走法,一种是通过公交绕道一个> K的位置再往后走,另一种是先往后走,再通过公交走到一个 < K的位置走上去。
  • 显然对于上面两种走法,当且仅当公交走过去的位置是K+1时,先公交再向后步行是更优的,否则先向后步行再往前做公交能够节约更多的时间。

三、代码

#include<iostream>
#include<queue>
using namespace std;
const int maxn = 100010, inf = 1e9+7;

int dist[maxn], vis[maxn]; //到达点i的最短路,是否使用过

int bfs(int st, int ed){
    int mx = max(st, ed)+10;
    for(int i = 0; i <= mx; i++)dist[i] = inf, vis[i] = 0;
    queue<int>q;

    dist[st] = 0;
    vis[st] = 1;
    q.push(st);
    while(q.size()){
        int t = q.front();  q.pop();
        // 步行
        for(int dx = -1; dx <= 1; dx++){
            if(dx == 0)continue;
            int nt = t+dx;
            if(nt<0 || nt >= mx)continue;
            if(dist[nt]>dist[t]+1){
                dist[nt] = dist[t]+1;
                if(!vis[nt]){
                    q.push(nt);
                    vis[nt] = 1;
                }
            }
        }
        // 公交
        int ntt = 2*t;
        if(2*t<=mx && dist[ntt]>dist[t]+1){
            dist[ntt] = dist[t]+1;
            if(!vis[ntt]){
                q.push(ntt);
                vis[ntt] = 1;
            }
        }
    }
    return dist[ed];
}

int main(){
    int n, k;  cin>>n>>k;
    int res = bfs(n, k);
    cout<<res<<"\n";
    return 0;
}