题目大意:在一个8*8的棋盘中,以马的走棋方式(与中国象棋一致,马踏日)给出起点和终点,问最短需要多少步可以走到。
解题思路:直接BFS就可以求解,但这里为了节约时间,可以采用双向BFS,其原理与BFS差不多,只是压入队列的时候,直接将起点和终点均压入,然后判断每个点的访问标记是否一致,不一致则说明双向的访问到达同一点。或者将起点和终点分别压入两个队列,从起点开始BFS访问标记置为1,终点BFS标记置为2。在正向BFS中判断,如果遇见标记为2,则说明相遇;反之反向BFS中同理可得。此时输出正向BFS的访问长度加上反向BFS访问长度即为所求的步数。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1372
BFScode:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 8;
int vis[MAXN][MAXN];
char x1,x2,a[3],b[3];
int sx,sy,ex,ey;
int dir[8][2]={{1,2},{2,1},{1,-2},{2,-1},{-1,-2},{-2,-1},{-1,2},{-2,1}};
struct node{
int x,y,step;
};
int BFS(){
node s,t;
queue<node> q;
s.x=sx;s.y=sy;//初始化操作
s.step=0;
q.push(s);
vis[s.x][s.y]=1;//访问则标记
while(!q.empty()){
node tmp=q.front();//获取队首元素
q.pop();
if(tmp.x==ex && tmp.y==ey) return tmp.step; //满足条件则输出
for(int i=0;i<8;i++){
t.x=tmp.x+dir[i][0];
t.y=tmp.y+dir[i][1];
if(t.x>=0 && t.x<8 && t.y>=0 && t.y<8 && !vis[t.x][t.y]){ //边界和条件判断
t.step=tmp.step+1;
q.push(t);
vis[s.x][s.y]=1;
}
}
}
return 0;
}
int main(){
while(scanf("%s%s",a,b)!=EOF){
sx=a[0]-'a';sy=a[1]-'1'; //起点和终点的接收
ex=b[0]-'a';ey=b[1]-'1';
memset(vis,0,sizeof(vis));
if(sx==ex && sy==ey)//相等则直接输出
printf("To get from %s to %s takes 0 knight moves.\n",a,b);
else{
int ans=BFS();
printf("To get from %s to %s takes %d knight moves.\n",a,b,ans);
}
}
return 0;
}
双向的code如下,但有一个问题是终点放不进去队列中,不知道为什么,始终只能读出起点,每次读取后都pop了,彻底无语了,调试了好久!原则上是没错的,始终找不到原因!(后边不甘心,重新用了一种写法见下一个code)
双向BFScode(报错了):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 8;
int vis[MAXN][MAXN];
int sx,sy,ex,ey;
int dir[8][2]={{1,2},{2,1},{1,-2},{2,-1},{-1,-2},{-2,-1},{-1,2},{-2,1}};
char a[3],b[3];
struct node{
int x,y,step;
};
int double_BFS(){
node s,e,t;
queue<node> q;
s.x=sx;s.y=sy; //起点初始化入队
s.step=0;
q.push(s);
vis[s.x][s.y]=1; //起点开始访问标记为1
e.x=ex;e.y=ey; //终点初始化入队
e.step=0;
q.push(e);
vis[e.x][e.y]=2; //终点开始访问标记为2
while(!q.empty()){
node tmp=q.front(); //获取队首元素
q.pop();
for(int i=0;i<8;i++){
t.x=tmp.x+dir[i][0];
t.y=tmp.y+dir[i][1];
if(t.x>=0 && t.x<8 && t.y>=0 && t.y<8 && !vis[t.x][t.y]){ //边界和条件判断
vis[t.x][t.y]=vis[tmp.x][tmp.y]; //将该次的标记标记为上次的标记
t.step=tmp.step+1;
q.push(t);
}
else if(vis[t.x][t.y]!=vis[tmp.x][tmp.y]) //如果同一个点标记不同,则说明双向访问第一次接触,即为结果
return t.step+tmp.step+1;
}
}
return 0;
}
int main(){
while(scanf("%s%s",&a,&b)!=EOF){
sx=a[0]-'a';sy=a[1]-'1'; //起点和终点的接收
ex=b[0]-'a';ey=b[1]-'1';
memset(vis,0,sizeof(vis));
if(sx==ex && sy==ey) //相等则直接输出
printf("To get from %s to %s takes 0 knight moves.\n",a,b);
else{
int ans=double_BFS();
printf("To get from %s to %s takes %d knight moves.\n",a,b,ans);
}
}
return 0;
}
最后还是不忍心自己的第一个双向BFS就这样挂了,改了一种写法就A了,看了下时间比开始的不是一般的快。
doubleBFScode:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 8;
int vis[MAXN][MAXN];
char a[3],b[3];
bool flag;
int sx,sy,ex,ey,ans,cnt,ans1,ans2;
int dir[8][2]={{1,2},{2,1},{-1,2},{-2,1},{-1,-2},{-2,-1},{1,-2},{2,-1}};
struct node{
int x,y,step;
};
node s,e;
queue<node> que,reque;
void BFS(){
node t;
node tmp=que.front();
que.pop();
for(int i=0;i<8;i++){
t.x=tmp.x+dir[i][0];
t.y=tmp.y+dir[i][1];
// if(t.x<0 && t.x>=8 && t.y<0 && t.y>=8) continue;
// if(vis[t.x][t.y]==1) continue;//这样判断是否越界和访问老是报错,直接改成下面的满足条件才执行,一下就好了
if(t.x>=0 && t.x<8 && t.y>=0 && t.y<8 && vis[t.x][t.y]!=1){
t.step=tmp.step+1;
ans1=t.step; //用ans1来记录正向的长度
if(vis[t.x][t.y]==2){ //如果该点标记为反向访问的标记,则说明相遇
flag=false;
ans=ans1+ans2; //相遇后长度为正向和反向之和
return ;
}
vis[t.x][t.y]=1;
que.push(t);
}
}
}
void inversionBFS(){
node ret;
node retmp=reque.front();
reque.pop();
for(int i=0;i<8;i++){
ret.x=retmp.x+dir[i][0];
ret.y=retmp.y+dir[i][1];
// if(ret.x<0 && ret.x>=8 && ret.y<0 && ret.y>=8) continue;
// if(vis[ret.x][ret.y]==2) continue;
if(ret.x>=0 && ret.x<8 && ret.y>=0 && ret.y<8 && vis[ret.x][ret.y]!=2){ //越界和访问条件判断
ret.step=retmp.step+1;
ans2=ret.step;
if(vis[ret.x][ret.y]==1){ //如果该点标记为正向访问的标记,则说明相遇
flag=false;
ans=ans1+ans2;
return ;
}
vis[ret.x][ret.y]=2;
reque.push(ret);
}
}
}
int main(){
while(scanf("%s%s",a,b)!=EOF){
s.x=a[0]-'a';s.y=a[1]-'1';
e.x=b[0]-'a';e.y=b[1]-'1';
s.step=0;e.step=0; //各种初始化
memset(vis,0,sizeof(vis));
flag=true;ans=0;
ans1=0,ans2=0;
while(!que.empty()) //每次都把队列清空
que.pop();
while(!reque.empty())
reque.pop();
que.push(s); //将起始点放入正向队列
vis[s.x][s.y]=1;
reque.push(e); //将终点放入反向队列
vis[e.x][e.y]=2;
if(s.x==e.x && s.y==e.y) //起点和终点相等,则直接输出
printf("To get from %s to %s takes 0 knight moves.\n",a,b);
else{
while(flag){
cnt=que.size();
while(cnt-- && flag) //队列非空以及访问标记判断
BFS(); //正向宽搜
cnt=reque.size();
while(cnt-- && flag)
inversionBFS(); //反向宽搜
}
printf("To get from %s to %s takes %d knight moves.\n",a,b,ans);
}
}
return 0;
}