题目大意:给定一个数N (O≤N≤100000),变成另一个数K(O≤K≤100000),允许的操作是乘以2,或者加减1,问最少要几步才能完成?
题目分析: 一般这种求有多少种方案的感觉用bfs 的居多。反正这题是用bfs的,然后广度优先搜索都是要用到队列的,然后感觉这题就比较裸吧,只是一维的,相对于之前做的那个抄作业(三维bfs),不过比较尴尬的是那个一直不知道哪地方错了。就题论题。这题就是先使满足条件的*2,+1或者-1,然后压入队列,让上一次的队头出队列。啊呀,感觉好难讲,还是结合代码讲吧,最好手算模拟一下,当初我也是理解不了,然后手算模拟的,才明白的。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
struct node{
int o,count;//o是表示当前的步数所到达的位置,count是统计到达此的步数
}q,p;
bool vis[400010];//作为标记数组,标记已经走过的点
int main(){
int n,k;
while(~(scanf("%d%d",&n,&k))){
p.o = n;//表示初始点的位置
p.count = 0;//表示到达此点时的最小步数
queue<node>bfs;//建一个队列
memset(vis,false,sizeof(false));//初始化标记数组的值,设为都没有访问过
bfs.push(p);//将p压入队列
vis[p.o ]=true; //标记初始点为已经访问过的点
while(!bfs.empty()){//除非队列为空,否则此循环不停止
p=bfs.front();//把队列的初始值赋值给p,每一次循环都要做,这个很重要。
if(p.o == k){
printf("%d\n",p.count );
break;
}//如果到达目的点,则中断,输出步数
p.count ++;//此点的步数加1,按照点的运动规则,到达下列的点
if(p.o < k){//如果大于k的话,就*2或者+1,这样可以省时
q=p;//先找一个q代替p,以保证下面的q的操作正常
q.o = p.o*2;
if(vis[q.o] ==false && q.o ){//如果此点没有被访问过,就更新成已经访问过的
vis[q.o]=true;
bfs.push(q);
}
q=p;
q.o ++;
if(vis[q.o]== false){
vis[q.o]=true;
bfs.push(q);
}
}
if(p.o > 0){//这个本来想是》k的,后来一想,还是》0对
q=p;
q.o --;
if(vis[q.o ]== false ){
vis[q.o ]=true ;
bfs.push(q);
}
}
bfs.pop(); //已经搜过的点就出队列。
}
}
return 0;
}