当青训营遇上码上掘金
题目大意
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。 步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1 公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
分析
公交车只能向前走,步行可以向前也可以向后。
如果,那么显然小青只能一步一步步行去见小码,需要的步数为,时间复杂度为。
如果,可以用一张有向图来表示这条直线,图上每个点向、、三个点各连一条有向边,边的长度为。小青从出发,到达最小花费的时间,就是图上点到点的最短路径。因为所有边的长度都为,可以在图上从点开始做一次BFS,求出到点的最短路径长度,时间复杂度为。因为小青有可能先做公交超过点,再步行折返回到点,所以图中需要多开一些点。题目数据规定,在图上开个点就足够了。
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;
}