「青训营 X 码上掘金」 | 主题3:寻友之旅

66 阅读2分钟

当青训营遇上码上掘金 本文是对「青训营 X 码上掘金」主题创作活动入营版 开启!活动的主题3:寻友之旅的题解文章。

题目详情:

主题 3:寻友之旅

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

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

C++代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;

int n, k, q[N], dist[N];
// q是队列,dist表示步数
int bfs(){
    memset(dist, -1, sizeof dist);
    dist[n] = 0;
    q[0] = n;

    int head = 0, tail = 0; // head是q队列的头指针吗,tail是q队列的尾指针
    while(head <= tail){
        int now = q[head ++ ];
        if(now == k) return dist[k]; // dist[k]就是k到n的最短路径
        if(now + 1 < N && dist[now + 1] == -1){
            dist[now + 1] = dist[now] + 1;
            q[ ++ tail] = now + 1;
        }
        if(now - 1 >= 0 && dist[now - 1] == -1){
            dist[now - 1] = dist[now] + 1;
            q[ ++ tail] = now - 1;
        }
        if(now * 2 < N && dist[now * 2] == -1){
            dist[now * 2] = dist[now] + 1;
            q[ ++ tail] = now * 2;
        }
    }
    return -1; // 虽然这题因为题意保证有解,但是根据代码严谨性,这里还是写了个return -1
}

int main()
{
    scanf("%d%d", &n, &k);
    printf("%d\n", bfs());
    return 0;
}

解题大致思路

这是一个很经典的BFS最短路问题,就是从N到K的最短路径。首先是数据范围,k (0≤n , k≤100 000)。先看左边界。我们先考虑会不会用到负数,答案是不会。大致证明是,假如跳到了负数坐标(节点),那么一定是利用了X到X-1这个唯一一种方法,但是k不会是负数,所以最终还是会从负数回到≥的范围,所以负数是不会考虑到的,直接pass。然后是有边界,也就是最大可能用到的坐标。我思考的有边界是k+1,因为如果当前坐标乘以2比k多2个及以上,那么我们可以先减再乘以2。

我们这时候就可以把这题看做一个图,所有的边权重都是1。因为所有边的权重都相同,所以可以用BFS求最短路,也可以看做一个特殊的dijkstra算法。题目中的三种情况,其实就是从X到X+1, X-1, X*2,各连一条边。然后BFS跑就可以了,因为是+1和-1,所以保证一定有解。时间复杂度O(k),这个就很明显了,最差也经过+1或者-1到k点了。