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

100 阅读2分钟

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

原题目

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

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

方法一:BFS

起点和终点固定,每次有三种移动方式,由 xxx1x - 1x+1x + 1x×2x \times 2,题目所求是最短时间,所以考虑最短路模型,这里用 BFS 求解,基本思路是:

  • 将起点放入队列中,用一个 vis 数组标记那些位置已经访问过了;
  • 每次循环的时候,先求出队列的大小,表示此时达到这些位置需要的最短时间相同;
  • 然后继续循环遍历这些位置,接着判断三种移动方式是否有效,若在有效区间内,放入队列中;
  • 直到达到终点。

详细代码如下:

#include <iostream>
#include <queue>
using namespace std;

const int N = 1e5 + 10;
int n, m, k, t;
bool vis[N];

// 确保不越界
bool check(int x) {
    return x >= 0 && x < N;
}

int main() {
    cin >> n >> k;
    queue<int> que;
    que.emplace(n);
    int cnt = 0;
    while (!que.empty()) {
        int sz = que.size();
        while (sz--) {
            auto x = que.front(); que.pop();
            if (vis[x]) continue;
            vis[x] = true;
            if (x == k) {
                cout << cnt << endl;
                return 0;
            }
            if (check(x - 1) && !vis[x - 1]) que.push(x - 1);
            if (check(x + 1) && !vis[x + 1]) que.push(x + 1);
            if (check(x * 2) &&!vis[x * 2]) que.push(x * 2);
        }
        ++cnt;
    }
    return 0;
}

方法二:Dijkstra求解最短路

求最短路也可以用经典的 Dijkstra 算法求解,这里用了一个 dist 数组表示每个位置的距离,初始时都设置为最大值(我这里用 memset 函数设置为 0x3f0x3f),然后将起点距离设置为 00,接下来和上面的代码有点像了。不过注意 C++ 中的优先队列如果存放 pair 类型是以第一个数据作为优先级排序的,所以为了每次取出优先队列中距离最小的,pair 第一个数据需要存放的是时间,第二个才是位置。详细见下面代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
using pii = pair<int, int>;
const int N = 1e5 + 10;

int n, k;
int dist[N];

// 确保不越界
bool check(int x) {
    return x >= 0 && x < N;
}

int dijkstra() {
    memset(dist, 0x3f, sizeof(dist));
    dist[n] = 0;
    priority_queue<pii, vector<pii>, greater<pii>> heap;
    heap.emplace(0, n);
    while (!heap.empty()) {
        auto [dis, x] = heap.top(); heap.pop();
        if (vis[x]) continue;
        vis[x] = true;
        if (check(x - 1) && dist[x - 1] > dis + 1) {
            dist[x - 1] = dis + 1;
            heap.emplace(dis + 1, x - 1);
        }
        if (check(x + 1) && dist[x + 1] > dis + 1) {
            dist[x + 1] = dis + 1;
            heap.emplace(dis + 1, x + 1);
        }
        if (check(x * 2) && dist[x * 2] > dis + 1) {
            dist[x * 2] = dis + 1;
            heap.emplace(dis + 1, x * 2);
        }
    }
    return dist[k];
}

int main() {
    cin >> n >> k;
    cout << dijkstra() << endl;
    return 0;
}