当青训营遇上码上掘金
题目:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 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