当青训营遇上码上掘金
寻友之旅
题目描述
小青要找小码去玩,他们的家在一条直线上,当前小青在地点,小码在地点,并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)请帮助小青通知小码,小青最快到达时间是多久?
输入格式
两个整数和
输出格式
小青到小码家所需的最短时间(以分钟为单位)
思路分析
我们根据和的大小分成两种情况讨论
- 当时,由于公交不可以向后走,因此小青只能通过步行向后走步才能抵达小码家地点,因此小青到小码家所需的最短时间为分钟
- 当时,我们可以通过或者,依次搜索小青可以到达的所有节点,直到找到小码所在的节点。显然,小青一定可以通过步行的方式抵达小码家地点,也就是一定可以搜索到小码所在的节点。
算法一 —— DFS
C++ 代码
#include<iostream>
using namespace std;
const int MAXV = 100000;
int n, k;
int res = MAXV; // 最短时间,初始为N和K距离的最大值
void dfs(int u, int times) {
if (times >= res) return; // 剪枝优化,当前时间大于已求最短时间
if (u == k) {
res = times;
return;
}
if (u * 2 <= MAXV) dfs(u * 2, times + 1); //小优化,必须先递归公交交通方式,否则会超时
dfs(u + 1, times + 1);
dfs(u - 1, times + 1);
}
int main() {
cin >> n >> k;
if (n >= k) res = n - k;
else dfs(n, 0); // 寻找最小值
cout << res << endl;
return 0;
}
代码解释
DFS中的参数u和times含义分别为当前小青处于的位置和已花费的时间当
u == k时,不需要取res和times的最小值,times大于res的情况已经通过剪枝优化掉了对于代码中小优化部分的解释:通过先递归公交交通方式,可以让小青快速从
N逼近K,可以快速求得一个较优的答案,减少递归层数。
算法二 —— BFS
C++ 代码
#include <iostream>
using namespace std;
const int N = 100000;
int n, k;
int times[N]; //每个距离对应的最短距离
int q[N * 2]; // 队列
int bfs() {
int hh = 0, tt = 0;
q[0] = n; // 将n入队
while (hh <= tt) {
int t = q[hh ++ ];
if (t == k) return times[k];
if (t * 2 <= N && !times[t * 2]) {
times[t * 2] = times[t] + 1;
q[++ tt] = t * 2;
}
if (t - 1 >= 0 && !times[t - 1]) {
times[t - 1] = times[t] + 1;
q[++ tt] = t - 1;
}
if (t + 1 <= N && !times[t + 1]) {
times[t + 1] = times[t] + 1;
q[++ tt] = t + 1;
}
}
}
int main() {
cin >> n >> k;
int res;
if (n >= k) res = n - k;
else res = bfs();
cout << res << endl;
return 0;
}
代码解释
由于
BFS是层次搜索,因此先搜索到的节点一定是最短的,所以当搜索到小码所在的节点时,直接返回答案即可。
算法三 —— 堆优化的BFS
C++ 代码
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int N = 100000;
int n, k;
int times[N]; //每个距离对应的最短距离
priority_queue<int, vector<int>, greater<int>> q; // 大根堆
int bfs() {
q.push(n); // 将n入队
while (q.size()) {
int t = q.top();
q.pop();
if (t == k) return times[k];
if (t * 2 <= N && !times[t * 2]) {
times[t * 2] = times[t] + 1;
q.push(t * 2);
}
if (t - 1 >= 0 && !times[t - 1]) {
times[t - 1] = times[t] + 1;
q.push(t - 1);
}
if (t + 1 <= N && !times[t + 1]) {
times[t + 1] = times[t] + 1;
q.push(t + 1);
}
}
}
int main() {
cin >> n >> k;
int res;
if (n >= k) res = n - k;
else res = bfs();
cout << res << endl;
return 0;
}
代码解释
通过前面的思路分析,我们仅需要在时使用
BFS求解。因此通过贪心,我们可以发现小青每次尽量走的点需要尽量接近k,因此这里使用大根堆来进行BFS。
算法对比
的空间复杂度为,时间复杂度为
的空间复杂度为,时间复杂度为
堆优化的的空间复杂度为,时间复杂度为,V为节点数
- 当数据范围较小时,推荐使用算法,因为代码简短,比较好写。
- 当数据范围较大时,推荐使用堆优化的算法,复杂度最低。