您的位置:首页 > 其它

BFS与双向BFS Knight Moves(经典棋盘问题!!!)

2017-07-16 10:16 381 查看
题意:棋盘上移动判断多少步可以到达(确认是可以到达的)

思路:BFS广度搜索,每一个点最多有8个可能的位置



然后依次“枚举”即可。

 该版本为队列中存贮的为int 型的数据,每一组数据要压入队列3次,相应的出队列也是三次

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
//定义方向数组dir 为8个方向的偏移量
struct position
{
int x,y;
};
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];
int a[6];
queue<int>que;
bool vis[9][9];
int ans;
bool in(int a,int b){
if(a>0&&a<=8&&b>0&&b<=8)
return true;
return false;
}

int BFS()
{
int col,row;//定义列,行,计数器
ans=0;
que.push(a[0]); //起点列
que.push(a[1]); //起点 行
que.push(ans);  //起点步数
vis[a[1]][a[0]]=true;
while(!que.empty())
{
col=que.front(); que.pop();
row=que.front(); que.pop();
ans=que.front(); que.pop();//取编号
if(col==a[2]&&row==a[3]) //到达目标状态
return ans;

for(int i=0;i<8;i++) //向8个方向扩展
{
//扩展入队列
if(in(row+dir[i].x,col+dir[i].y)&&!vis[row+dir[i].x][col+dir[i].y]){
que.push(col+dir[i].y);
que.push(row+dir[i].x);
que.push(ans+1);
vis[row+dir[i].x][col+dir[i].y]=true;

}

}
}
}

int main()
{
int i,j;
while(gets(c))
{
while(!que.empty())
que.pop();
for(i=0;i<=8;i++)
for(j=0;j<=8;j++)
vis[i][j]=false;
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[2]=c[3]-'a'+1;
a[3]=c[4]-'0';
BFS();
printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
}
return 0;
}




此版本是使用了结构体之后,把之前版本的一组三个数据放在结构体node中,队列类型为node ,所以一组数据入出队列以次即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct node
{
int x,y,sum;
};
struct position
{
int x,y;
};
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];
int  a[6];
queue <node> que; //什么类型<>中就放入什么
bool vis[10][10];
int ans;
bool in(int x,int y)
{
if(x<=8&&x>0&&y>0&&y<=8)
return true;
return false;
}
int BFS()
{
ans=0;
node t,m;
t.x=a[0]; t.y=a[1]; t.sum=ans;
memset(vis,0,sizeof(vis));  //初始化访问数组
vis[t.x][t.y]=1;  //初始点初始化
que.push(t);    //初始点压入队列
while(!que.empty())
{
t=que.front(); que.pop();
if(t.x==a[3]&&t.y==a[4])
{
ans=t.sum;
// printf("%d",ans);
return ans;   //到达目标位置,返回
}

for(int i=0;i<8;i++)
{
if(in(t.x+dir[i].x,t.y+dir[i].y)&&!vis[t.x+dir[i].x][t.y+dir[i].y])
{
m.x=t.x+dir[i].x;
m.y=t.y+dir[i].y;
m.sum=t.sum+1;
que.push(m);
}
}
}

}
int main()
{
while(gets(c))
{
while(!que.empty())
que.pop();
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[3]=c[3]-'a'+1;
a[4]=c[4]-'0';
BFS();

printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
}
return 0;
}



下面是双向BFS的数组版本,但是有bug,这里给出代码的大体思路。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct position
{
int x,y;
};
//定义方向数组dir
position dir[8]={{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2}};
char c[6];
int a[6];
queue<int> que1,que2;
int vis[9][9];
int cnt,ans,ans1,ans2;
int  flag;
bool in (int a,int b)
{
if(a>0&&a<=8&&b>0&&b<=8)
return true;
return false;
}
//此题目中输入的行列顺序为先列后行
void single_BFS1()
{
int col,row,stp,i;
col=que1.front();  que1.pop();
row=que1.front();  que1.pop();
stp=que1.front();  que1.pop();
for(i=0;i<8;i++)
{
if(in(row+dir[i].x,col+dir[i].y)&&vis[row+dir[i].x][col+dir[i].y]!=1)
{
ans1=stp+1;
if(vis[row+dir[i].x][col+dir[i].y]==2)
{
flag=0;
ans=ans1+ans2;
return ;
}
}
que1.push(col+dir[i].y);
que1.push(row+dir[i].x);
que1.push(ans2);
vis[row+dir[i].x][col+dir[i].y]=1;
}
}
void single_BFS2()
{
int col,row,stp,i;
col=que2.front();  que2.pop();
row=que2.front();  que2.pop();
stp=que2.front();  que2.pop();
for(i=0;i<8;i++)
{

if(in(row+dir[i].x,col+dir[i].y)&&vis[row+dir[i].x][col+dir[i].y]!=2)
{
ans2=stp+1;
if(vis[row+dir[i].x][col+dir[i].y]==1)
{
flag=0;
ans=ans1+ans2;
return ;
}
}
que2.push(col+dir[i].y);
que2.push(row+dir[i].x);
que2.push(ans2);
vis[row+dir[i].x][col+dir[i].y]=2;
}
}
int main()
{
while(gets(c))
{
while(!que1.empty())  que1.pop();
while(!que2.empty())  que2.pop();
for(int i=0;i<=8;i++)
for(int j=0;j<=8;j++)
vis[i][j]=0;
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[2]=c[3]-'a'+1;
a[3]=c[4]-'0';
flag=1; ans=ans1=ans2=0;
int step=0;

que1.push(a[0]);//起点入队列
que1.push(a[1]);
que1.push(step);
vis[a[1]][a[0]]=1;

que2.push(a[2]);//终点入队列
que2.push(a[3]);
que2.push(step);
vis[a[3]][a[2]]=2;
if(a[0]==a[2]&&a[1]==a[3]){
printf("To get from %c%c to %c%c takes 0 knight moves.\n",c[0],c[1],c[3],c[4]);
}
else{
while(flag)
{
cnt=que1.size()/3;  //统计本层节点个数,正向BFS
while(cnt--&&flag)  //本层节点为扩展完,且未相遇标志flag为true
single_BFS1();  //正向BFS

cnt=que2.size()/3;//统计本层节点个数,正向BFS
while(cnt--&&flag)//本层节点为扩展完,且未相遇标志flag为true
single_BFS2();//反向BFS
}
printf("%d %d %d\n",ans,ans1,ans2);
//printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);

}
}
return 0;
}


此为结构体型的双向BFS,值得学习的不仅仅是双向BFS的思路,还有将起始BFS与末尾BFS写在一起的思路。

这是这份代码最值得学习的,起始末尾BFS通用的地方通过函数传递过去,如果需要改变数值的话,加上引用即可

#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
struct position
{
int x,y;
};
struct node
{
int x,y,sum;
};
queue <node> que1,que2;
position dir[8]={{2,1},{2,-1},{-2,1},{-2,-1},{1,-2},{1,2},{-1,-2},{-1,2}};
int vis[10][10];  //1代表的是起始点的BFS 2代表的是结束点的BFS
int flag,ans,ans1,ans2;
char c[6];
int  a[6];

bool in(int a,int b)
{
if(a>0&&a<=8&&b>0&&b<=8)
return true;
return false;
}
void DBFS(queue<node>&que,int m,int n,int &res1,int res2)
{//m为起始 末位BFS vis所对应的 ,n则和m相对
//res为起始 末尾BFS所对应的ans

node now,next;
int x,y,stp;
now=que.front();  que.pop();
x=now.x,y=now.y;
stp=now.sum;
for(int i=0;i<8;i++) //从8个方向扩展
{
int row=now.x+dir[i].x,col=now.y+dir[i].y;
if(in(row,col)&&vis[row][col]!=m)
{
res1=stp+1;
if(vis[row][col]==n) //判断是否“相遇”
{
ans=res1+res2;
flag=0;
return ;
}
next.x=row,next.y=col;
next.sum=res1;
que.push(next);  //将next压入队列
vis[row][col]=m; //进行标记
}
}

}
int main()
{
while(gets(c))
{
while(!que1.empty())
que1.pop();
while(!que2.empty())
que2.pop();

memset(vis,0,sizeof(vis));
a[0]=c[0]-'a'+1;
a[1]=c[1]-'0';
a[2]=c[3]-'a'+1;
a[3]=c[4]-'0';

flag=1;
ans=ans1=ans2=0;
node s,e; //起始点与结束点

s.x=a[1],s.y=a[0];
s.sum=0;
que1.push(s);
vis[s.x][s.y]=1;

e.x=a[3],e.y=a[2];
e.sum=0;
que2.push(e);
vis[e.x][e.y]=2;
//数据的初始化完成

if(a[1]==a[3]&&a[0]==a[2])
printf("To get from %c%c to %c%c takes 0 knight moves.\n",c[0],c[1],c[3],c[4]);
else
{
while(flag)
{
int num1=que1.size();  //统计本层的节点个数
while(num1--&&flag) //本层节点为扩展完且标识符flag为1
DBFS(que1,1,2,ans1,ans2);

int num2=que2.size();  //统计本层的节点个数
while(num2--&&flag)  //本层节点为扩展完且标识符flag为1
DBFS(que2,2,1,ans2,ans1);
}
printf("To get from %c%c to %c%c takes %d knight moves.\n",c[0],c[1],c[3],c[4],ans);
}

}
}



双向BFS:起始BFS一层,末尾BFS一层,在循环当中判断是否相遇,直到flag为0.
此为效率最高的算法,当面对数据范围较大的题目时,优势将明显体现出来
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: