当青训营遇上码上掘金

63 阅读2分钟

主题 3:寻友之旅

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

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

解法

可以将上面问题转化为:给定初始数字 num 和目标数字 target,求最少需要多少次操作才能从 num 变成 target

每次操作只能选择以下三种:

  • 将当前数字减一。
  • 将当前数字加一。
  • 将当前数字乘二。

为了保证不重复计算,使用一个布尔数组 isVisited 记录已经到达过的数字。

广搜的思路就是不断地从队列的队首取出一个数字,对它进行上面三种操作并将新生成的数字加入队列,直到队列为空或者找到了目标数字。

在本题中,用队列存储数字和到达该数字所需操作次数,以便在访问过程中对答案进行更新。

最终结果就是到达目标数字的操作次数,在代码中用 result 变量存储。

代码

#include<bits/stdc++.h>
using namespace std;

const int Max = 1e5 + 5;
bool isVisited[Max];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int num, target;
    cin >> num >> target;
    queue<pair<int, int>> q;
    q.push({num, 0});
    int result = INT_MAX;
    memset(isVisited, false, sizeof isVisited);
    while (!q.empty()) {
        pair<int, int> tmp = q.front();
        q.pop();
        if (isVisited[tmp.first]) continue;
        isVisited[tmp.first] = true;
        if (tmp.first == target) {
            result = tmp.second;
            break;
        }
        if (tmp.first > 0 && tmp.first < Max) q.push({tmp.first - 1, tmp.second + 1});
        if (tmp.first < Max - 1) q.push({tmp.first + 1, tmp.second + 1});
        if (tmp.first < target) q.push({tmp.first * 2, tmp.second + 1});
    }
    cout << result << endl;
    return 0;
}