HDU-#1372 Knight Moves(双向BFS)

题目大意:在一个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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值