题目链接
早上也写了一篇这道题关于双向广搜的题解,但那个写法有一个漏洞,而且很慢,在下面我将一一道来。我们知道,单向广搜时由起始点出发,引出4个分支,再由4个分支引出16个分支,可以看出这个增长速度是非常快的,而且随着搜索层数递增。于是加入双向广搜进行时间优化,双向广搜是由起点和终点两个点出发不断交替引出分支,当两者的分支发生碰撞时就可以得出答案了。重要的是”交替”两个字,不是指两边交替着一个一个引出分支,而是一层一层地引出分支(这里指一个步数一个步数地引出),原因很简单,一个一个引出分支可能会导致答案更大,也就是原先可以以x+1步引出的数,现在要以x+2步引出,显然错误。
另外还有三个优化的方法:1.每次BFS前不需要将step初始化,因为一个数的visit如果更新了,那么它的step也会更新;2.其实visit也不需要初始化,只要每次记下这是第几组数据,然后给对应的正向搜索和反向搜索编上号就行了;3.可以将系统队列转化为自定义的数组,这显然可以加快时间,而且可以只开在一个数组里,只要一个由左往右,一个由右往左就行了。
下面给出代码。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define M 2000005
using namespace std;
template <class T>
inline void Rd(T &res){
char c;res=0;int k=1;
while(c=getchar(),c<48&&c!='-');
if(c=='-'){k=-1;c='0';}
do{
res=(res<<3)+(res<<1)+(c^48);
}while(c=getchar(),c>=48);
res*=k;
}
bool found;
int Q[M];
int visit[M];//存节点是否被遍历过,0为正向遍历过,1为反向遍历过,-1为未遍历
int step0[M];//正向搜索的步数保存
int step1[M];//反向搜索的步数保存
int res;//搜索到的共同节点
bool check(int k,int x){
if(k==0)return x+1<M;
if(k==1)return x-1>0;
if(k==2)return 2*x<M;
return !(x&1);
}
int f(int k,int x){
if(k==0)return x+1;
if(k==1)return x-1;
if(k==2)return 2*x;
return x/2;
}
void bfs(int &L,int &R,bool flag,int time){
int x;
if(!flag)x=Q[L++];
else x=Q[R--];
int step;
if(!flag)step=step0[x];
else step=step1[x];
for(int i=0;i<4;i++)
if(check(i,x)){
int nx=f(i,x);
if(!flag){//在正向队列中判断
if(visit[nx]!=time){//未在正向队列中出现
step0[nx]=step+1;
if(visit[nx]==time+1){
found=true;
res=nx;
return;
}
visit[nx]=time;
Q[R++]=nx;
}
}else{//在反向队列中判断
if(visit[nx]!=time+1){//未在反向队列中出现
step1[nx]=step+1;
if(visit[nx]==time){
found=true;
res=nx;
return;
}
visit[nx]=time+1;
Q[L--]=nx;
}
}
}
}
void BFS(int s,int t,int time){
found=false;
visit[s]=time,visit[t]=time+1,step0[s]=0,step1[t]=0;
int L0=0,R0=0,L1=1999999,R1=1999999;
Q[R0++]=s;Q[L1--]=t;
while(L0<R0||L1<R1){
int R=R0;
while(L0<R)bfs(L0,R0,0,time);
if(found)return;
int L=L1;
while(L<R1)bfs(L1,R1,1,time);
if(found)return;
}
}
int T,s,t;
int main(){
memset(visit,-1,sizeof(visit));
Rd(T);
while(T--){
Rd(s);Rd(t);BFS(s,t,T<<1);
printf("%d\n",2*(step0[res]+step1[res]));
}
return 0;
}
另外,这里有一个提醒:慎用位运算,因为位运算的优先级有一些古怪,容易造成一些错误,博主就因为这个问题WA了好多次。
此题虐我千百遍,誓待此题如初恋/(ㄒoㄒ)/~~