当青训营遇上码上掘金 | 主题 3:寻友之旅
当青训营遇上码上掘金 | 主题 3:寻友之旅
一、题目描述
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
测试数据:
3 5
2
10 1000
11
99 99999
15
二、解题思路
这道题目我们可以使用广度优先搜索BFS算法解决,具体做法如下:
首先我们从小青所在的地点出发,依次搜索小青可以到达的所有位置,并不断的加入待访问队列中,直到我们找到小码所在的位置。
具体实现:
- 我们定义状态为:某个具体的位置
- 则起始状态为小青所在的位置,终止状态为小码所在的位置。
- 每次转移有两种方式:分别是步行和公交
- 对于步行,我们将小青当前所在的位置+1或-1,如果在范围内,并且没有转移过,那就转移过去。
- 对于公交,我们首先判断是否超过最远距离,然后按照步行的方式转移即可。
- 然后我们维护一个dist数组,表示从小青的初始位置出发,到达位置i时需要用的最短时间,那么到达小码的位置的最短时间就是
dist[K]。 - 对于最远的距离,明显是不会大于K+1的,考虑当N < K的时候,小青有两种走法,一种是通过公交绕道一个> K的位置再往后走,另一种是先往后走,再通过公交走到一个 < K的位置走上去。
- 显然对于上面两种走法,当且仅当公交走过去的位置是K+1时,先公交再向后步行是更优的,否则先向后步行再往前做公交能够节约更多的时间。
三、代码
#include<iostream>
#include<queue>
using namespace std;
const int maxn = 100010, inf = 1e9+7;
int dist[maxn], vis[maxn]; //到达点i的最短路,是否使用过
int bfs(int st, int ed){
int mx = max(st, ed)+10;
for(int i = 0; i <= mx; i++)dist[i] = inf, vis[i] = 0;
queue<int>q;
dist[st] = 0;
vis[st] = 1;
q.push(st);
while(q.size()){
int t = q.front(); q.pop();
// 步行
for(int dx = -1; dx <= 1; dx++){
if(dx == 0)continue;
int nt = t+dx;
if(nt<0 || nt >= mx)continue;
if(dist[nt]>dist[t]+1){
dist[nt] = dist[t]+1;
if(!vis[nt]){
q.push(nt);
vis[nt] = 1;
}
}
}
// 公交
int ntt = 2*t;
if(2*t<=mx && dist[ntt]>dist[t]+1){
dist[ntt] = dist[t]+1;
if(!vis[ntt]){
q.push(ntt);
vis[ntt] = 1;
}
}
}
return dist[ed];
}
int main(){
int n, k; cin>>n>>k;
int res = bfs(n, k);
cout<<res<<"\n";
return 0;
}