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

90 阅读2分钟

当青训营遇上码上掘金

题目:寻友之旅

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

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

解析

这道题目给我们的信息是,一直X点,X点与其它三个点之间有连线,并且到达另外三个点的时间花费均为1,即:X -> X + 1,X -> X - 1,X -> 2X;所以我们可以根据这个信息来构建一个有向图,而题目需要我们求的是点N到点K的最短距离。这时我们可以很轻松的想到迪杰斯特拉。看到题目要求中,N和K的数量级是1e5,所以朴素版Dijkstra一定无法满足要求,这时我们可以想到堆优化版本的Dijkstra。

我们可以用一个邻接表来存储图,然后借助优先队列来记录每一个未被拓展的点的距离。遍历完一整个图后,即可的值起点到任意一个点的最短距离。

代码

//堆优化版的迪杰斯特拉
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
typedef pair<int, int> PII;
priority_queue<PII,vector<PII>,greater<PII>> q;//按照距离从小到大排列
int N, K;//N -> K
vector<int> edge[maxn];
int dis[maxn];
bool vis[maxn];

void Dij()
{
    memset(dis,0x3f,sizeof dis);//将dis数组的值设置为无穷大
    dis[N] = 0;//起点到起点的值为0
    q.push({0,N});
    while(q.size()) {
        auto t = q.top();
        q.pop();
        int x = t.second;
        if(vis[x]) continue;
        vis[x] = true;
        for(int i = 0;i < edge[x].size();i ++) {
            int a = edge[x][i];
            if(dis[a] > 1 + dis[x]) {
                dis[a] = 1 + dis[x];
                q.push({dis[a],a});
            }
        }
    }
    cout << dis[K] << endl;
}

int main() {
    cin >> N >> K;
    //X -> X+1    X -> X - 1      X -> 2X
    for(int i = 1;i <= 200000;i ++) {
        edge[i].push_back(i + 1);
        edge[i].push_back(i - 1);
        if(i <= 100000) edge[i].push_back(2 * i);
    }
    Dij();
    return 0;
}

测试用例:

4 7

输出结果

2