当青训营遇上码上掘金 寻友之旅题解

57 阅读2分钟

当青训营遇上码上掘金

题目大意

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

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

分析

公交车只能向前走,步行可以向前也可以向后。

如果NKN \ge K,那么显然小青只能一步一步步行去见小码,需要的步数为NKN-K,时间复杂度为O(1)O(1)

如果N<KN < K,可以用一张有向图来表示这条直线,图上每个点XXX1X-1X+1X+12×X2 \times X三个点各连一条有向边,边的长度为11。小青从NN出发,到达KK最小花费的时间,就是图上NN点到KK点的最短路径。因为所有边的长度都为11,可以在图上从NN点开始做一次BFS,求出到KK点的最短路径长度,时间复杂度为O(n)O(n)。因为小青有可能先做公交超过KK点,再步行折返回到KK点,所以图中需要多开一些点。题目数据规定K100000K \le 100000,在图上开200000200000个点就足够了。

BFS求最短路

开一个数组dis[maxn], dis[i]表示从起点到i和最短路径长度。初始状态下,起点dis为0,其他点dis为无穷大。

开一个队列,将起点入队。

从队列队首取出一个点u,遍历u的所有出边连接的点v,如果dis[v] > dis[u] + 1,就把dis[v]更新为dis[u]+1,并把v入队。不断重复此操作,直到队列为空或终点出队。

在这个算法中,每个点会出队一次,然后遍历它的所有出边,时间复杂度为O(点数+边数)

代码实现

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int maxn = 200000 + 100;

int dis[maxn];
bool inq[maxn];
int main() {
    //初始化,到所有点的最短路径初始为无穷大
    memset(dis, 0x3f, sizeof(dis));
    int n, k;
    cin >> n >> k;
    dis[n] = 0;


    //BFS
    queue<int> Q;
    Q.push(n);

    while(Q.size()) {
        int u = Q.front();
        Q.pop();
        if(u == k) {
            break;
        }
        if(u > 0 && dis[u - 1] > dis[u] + 1) {
            dis[u - 1] = dis[u] + 1;
            Q.push(u - 1);
        }
        if(dis[u + 1] > dis[u] + 1) {
            dis[u + 1] = dis[u] + 1;
            Q.push(u + 1);
        }
        if(u <= 100000 && dis[u * 2] > dis[u] + 1) {
            dis[u * 2] = dis[u] + 1;
            Q.push(u * 2);
        }

    }
    cout << dis[k] << endl;
    return 0;
}