当青训营遇上码上掘金-寻友之旅

328 阅读3分钟

当青训营遇上码上掘金

本文为第五届字节跳动青训营主题创作活动之主题 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;
}