寻码之旅 | 「青训营 X 码上掘金」主题创作

67 阅读3分钟

当青训营遇上码上掘金

引言

参加第五届青训营(后端基础班)已经有一段时间了,关于Go语言的学习也有了些许收获。这篇文章主要是讲解我是如何编写“青训营X码上掘金”中的第三个主题(寻友之旅),如有错误,希望各位掘友能够指正。

题目

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走) 请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

分析

总结题目:即如何通过两种交通方式(步行和公交),从N点出发到达K点且耗时最短。

多种情况:

  • 若K<N,即小青需要向后走,那么就只能选择步行(公交不能向后走)
  • 若K=N,即已经到达小码所在位置
  • 若K>N,即小青需要向前出发到达K点,这也是这道题目的主要难点

根据上述情况,我个人判断应该是需要使用搜索,而搜索又可分为深度优先搜索(dfs)和广度优先搜索(bfs),两者的空间复杂度都是一致的,前者需要栈(递归),后者需要使用队列。

本题是需要找到最快到达的时间(最短耗时),这时若还采用深度优先搜索可能会导致程序耗时过长(dfs需要搜索所有情况,才会结束),所以这题我们应该采用广度优先搜索,当bfs到达K点时,程序就可结束,这时所需的时间,一定是最短的时间(bfs是一层一层来搜索的)。

注意:可能有先坐公交车,然后再向后步行。

代码实现

目前只采用了C++来实现,但算法的思想和步骤,不管采用任何语言都是一样的。

#include<iostream>
#include<set>
#include<queue> 
using namespace std;
bool inSet(set<int>& vis,int pt){
  if(vis.count(pt)==0) //还未经过此点 
  {
	 vis.insert(pt);
	 return true;
  }	
  return false;
}
int bfs(queue<int>& que,set<int>& vis,int K)
{
  int ans=0; 
  while(!que.empty()){
    int len=que.size();
    for(int i=0;i<len;i++){
      int p=que.front();
      que.pop();
      //分三种情况 
      if(p==K)
       return ans;
      else if(p>K){ //小码在小青的后面,那么只能选择步行 
         if(inSet(vis,p-1))
            que.push(p-1);	
      }
      else{ //分为步行 和 公交车 
        if(inSet(vis,p+1))
          que.push(p+1);	
        if(inSet(vis,p*2))
          que.push(p*2);	
      } 
    }
    ans++;
  } 
  return ans;
} 
int main() 
{
  int N,K;
  cin>>N>>K;
  if(N>=K){
     cout<<"最短耗时:"<<N-K<<"分钟!"<<endl;
     return 0;
  }
  queue<int> que;
  set<int> vis;  //是否已经到达过此点 
  vis.insert(N);
  que.push(N);
  int ans=bfs(que,vis,K); //广度优先搜索
  cout<<"最短耗时:"<<ans<<"分钟!"<<endl;
  return 0;
}

总结

以上就是我做这道编程题的步骤,灵感和分析过程。 当我们遇到搜索的题目,我们总是会想到深度优先搜索和广度优先搜索,这里可以总结当遇到题目求最短的路径,最小次数和最短时间等问题时,应该优先要考虑使用广度优先搜索。

希望这篇文章,能够给jym带来一些解题灵感,若发现了我的错误,请进行指正。