「青训营 X 码上掘金」主题创作之寻友之旅

70 阅读2分钟

当青训营遇上码上掘金,青训营高质量的课程立刻就会和码上掘金强大的代码编辑产生反应,制造出1+1>2的奇妙效果


主题 3:寻友之旅

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

请帮助小青通知小码,小青最快到达时间是多久?

输入: 两个整数 N 和 K 输出: 小青到小码家所需的最短时间(以分钟为单位)

思路

首先我们观察可以得出,这是一个最短路径问题,并且,每一条边权值相同

那我们可以用BFS(广度优先搜索)来解决

因为在 BFS 中,我们使用了数据结构中的队列(queue),我们知道队列的特性是 FIFO(First In First Out),也就是先进先出。正是这个 FIFO 特性,保证了我们第一个到达目标节点一定是最短路径

过程如下:

  • 首先观察可以得出,当n>=k时,不能坐公交(除了[0,0]),只能走路,因此可以直接特判返回n-k

  • 维护d[i]q[i]

    数组d[i]记录从起点到i点的距离,并且当d[i]=-1时,表示这个i点还没有被搜索过;

    队列q[i]则记录接下来要搜索的点,队空时结束搜索

  • 每次取队头结点t作为当前起点,尝试三种走法:ntx= [t+1t-1t*2],然后当目的地点在范围内(nxt<=k)并且这个点没有被搜索过(d[nxt]==-1),那么可以进行距离更新,然后 ntx入栈

  • 直到t==k,说明k点已经被搜索过了,可以直接结束搜索然后返回结果d[k]

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;
​
int d[N+1];   //存距离
int que[N+1]; //存将要走的点
​
int bfs(int n,int k){
    int hh=0,tt=0;
    que[0]=n;//从n开始
    memset(d,-1,sizeof(d)); //初始化为-1 表示还未走过
    d[n]=0;//点n已经走过
​
    while(hh<=tt){    //队列不空时继续搜
        auto t=que[hh++];//每次取队头
        if(t==k){//到达目的地
            return d[k];
        }
        for(int i=0;i<=2;i++){//三种走法
            int nxt;
            if(i==0) {
                nxt=t+1;
            } else if(i==1) {
                nxt=t-1;
            } else if(i==2) {
                nxt=t*2;
            }
            if(d[nxt]==-1&&nxt<=N){
                d[nxt]=d[t]+1;//距离更新
                que[++tt]=nxt;
            }
        }
    }
    return d[k];
}
​
int main(){
    int n,k;
    cin>>n>>k;
    if(n>=k){
        cout<< n-k;
        return 0;
    }
    cout<<bfs(n,k);
    return 0;
}