当青训营遇上码上掘金
本文为第五届字节跳动青训营主题创作活动之主题 3:寻友之旅的题目解答思路。
题目概述
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
思路讲解
这道题给出了起点、终点以及小青行走的方式,让我们求出到达终点的最短时间。很明显,这是一个典型的最短路径问题,而且可以发现每条边的边权为1,点的范围可以接受,所以并不需要使用更复杂的最短路径算法,只需要考虑bfs(广度优先搜索)的方法就可以快速求出最短路径。
bfs是最简便的图的搜索算法之一,所谓广度,就是一层一层地向下遍历。由于我们会先搜索深度低的层,再搜索更深的层,所以越早遍历到的点花费的时间肯定越短。由此可知,当我们第一次遍历到终点时,即可输出答案。
为了实现bfs,可以使用c++中的queue,一开始只将起点入队,由于1分钟内有三种走法,分别为u-1,u+1,u*2,所以可以将这三个点入队,它们的层数即为u的层数+1.
因此,我们还需要一个数据结构来存储点的层数,我在这里使用了unordered_map来同时实现记录层数和防止重复遍历的功能。在把一个点入队之前,首先要判断它是否已经存在于map中,如果已经存在,便证明这个点已经遍历到了,可以直接跳过;否则,就要将这个点入队,以及将这个键值对加入到map之中。
如果在遍历的过程中找到了k,便可以直接return;如果直到队列中的元素取完还没有找到k,便在最后return -1表示找不到(本题不存在这种情况)。
经过多组测试后,暂未发现代码输出错误的情况(如有问题请指正)。
代码实现
#include<iostream>
#include<queue>
#include<unordered_map>
int bfs(int n,int k)
{
if(n==k) return 0;
std::queue<int> q;
std::unordered_map<int,int> m;
m[n]=0;
q.push(n);
while(!q.empty())
{
int u=q.front();
q.pop();
if(u-1==k||u+1==k||u*2==k) return m[u]+1;
if(m.find(u-1)==m.end())
{
m[u-1]=m[u]+1;
q.push(u-1);
}
if(m.find(u+1)==m.end())
{
m[u+1]=m[u]+1;
q.push(u+1);
}
if(m.find(u*2)==m.end())
{
m[u*2]=m[u]+1;
q.push(u*2);
}
}
return -1;
}
int main()
{
int n,k;
std::cin>>n>>k;
std::cout<<bfs(n,k);
return 0;
}