此题题意是,在一个一维数轴上有一头牛,John现在要把它抓回来。输入n和k两整数,此牛在k位置上,John在n位置上,John可以做三种移动,假设他现在在位置n上,那么他分别可以移动到n+1, n-1, 2*n的位置上,求John抓到牛所需的最小步数。那么此类求最小步长的题目可以想到用BFS求解。先看代码:
#include<iostream>
#include<cmath>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
struct node{
int pos;
int step;
node (int x, int y){
pos=x;
step=y;
}
};
queue<node>q;
int n,k,ans=0,now;
int vis[200010];
void Init() {
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
ans=0;
q.push(node(n,0));
vis[n]=1;
}
void BFS(){
while(!q.empty()){
node temp=q.front();
q.pop();
for(int i=0;i<3;i++){
if(i==0) now=temp.pos+1;
if(i==1) now=temp.pos-1;
if(i==2) now=temp.pos*2;
int time=temp.step+1;
if(now>=0&&now<=200000&&!vis[now]){
vis[now]=1;
q.push(node(now,time));
ans=time;
}
if(now==k){
ans=time;
return;
}
}
}
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>k) {
if(n==k){
cout<<"0"<<endl;
continue;
}
Init();
BFS();
cout<<ans<<endl;
}
return 0;
}
首先要设法维护John的位置状态,那么可以用一个结构体来表示,用队列维护,pos表示当前位置,step表示这个状态下已经走过多少步(此处用了构造函数来初始化结构体,较为方便,可以参考),随后在初始化函数里将初始状态压入队列。
重点在BFS函数里。我们先要取出队首元素,赋给temp,由于John有三种移动状态,那么我们需要用一个3次的循环来表示三种移动,可以看到那三行if语句中体现了三种移动模式。随后用变量now存储移动后当前位置,time存储移动后已走的步长,若当前位置没有走出数轴且未搜过(注意!此处有一大坑:虽然题目给定的n、k范围是1e5,但实际上由于now*2这种移动方式的存在,搜索的数轴范围实际上是2e5,因此vis数组也要开两倍大,不然会RE),就可以搜索,接下来就是不断向前搜的过程,直到搜到k为止。
这里需要关注BFS使用队列的原理。你可能会问:有这么多移动方式,为何能保证最后得到的ans一定是最小值?这里的队列中节点是即取即更新,而由于队列FIFO(先进先出)的性质,进入队列的元素是按照步长递增的,而又保证了搜到k时直接输出,那么搜到k时输出的ans必定是即时搜到的最短步长,因此可以证明该算法能够得到最优解。