bfs 万圣节后的鬼屋

双向bfs(什么魔鬼题)

双向BFS本身不难,这道题的处理比较巧妙

1、为了方便代码处理,鬼数不足3的时候也要填充

2、首先如何表示一个鬼当前在的位置:将每个可以在的位置设置标号

3、如何表示一个状态(即三个鬼的位置):将三个鬼所在标号进行处理成一个能标识这三个值的唯一值 二进制压缩

这个比较难想,可以把三个鬼的位置开成结构体扔进队列里面

4、队列中存放的是一个状态,一个数字,如何标记该状态已经被使用过:d1[N][N][N]来标记
//又双叒叕死在  死因dist 和color数组开小了  挺好的

//读入的时候忘记读回车,感觉自己还是太菜辽  读入都不会


/*
与上面版本都是550ms
*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=20;
    const int M=200;
    int deg[M];
    int G[M][5];    //每个点最多有5个可留的位置
    int dx[5]={1,-1,0,0,0};
    int dy[5]={0,0,1,-1,0};
    int d1[M][M][M];
    int d2[M][M][M];
    char a[N][N];
    int s[3],t[3];
    int w,h,n;
    int ID(int a,int b,int c)
    {
        return (a<<16)|(b<<8)|c;
    }
    bool conflict(int a1,int b1,int a2,int b2)      //分别代表鬼A和鬼B的原位置和目标位置
    {
        return (a2==b2)||(a1==b2)&&(a2==b1);        //两只鬼要到达同一位置或者要交换位置,这种操作是不允许的
    }
    int bfs()
    {
        queue<int>q1,q2;
        q1.push(ID(s[0],s[1],s[2]));
        q2.push(ID(t[0],t[1],t[2]));
        d1[s[0]][s[1]][s[2]]=0;//三个鬼到达某一状态需要多少步
        d2[t[0]][t[1]][t[2]]=0;//该版本记录从后往前多少步

        while(!q1.empty()||!q2.empty())//不是&&
        {
            int fnum1=q1.size(),fnum2=q2.size();
            while(fnum1--)
            {
                int u=q1.front();q1.pop();
                int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;  //恢复三只鬼的位置在的编号
                if(a==t[0]&&b==t[1]&&c==t[2])  //有了快了10ms
                    return d1[a][b][c];
                for(int i=0;i<deg[a];++i)
                {
                    int a2=G[a][i];
                    for(int j=0;j<deg[b];++j)
                    {
                        int b2=G[b][j];
                        if(conflict(a,b,a2,b2)) continue;
                        for(int k=0;k<deg[c];++k)
                        {
                            int c2=G[c][k];
                            if(conflict(a,c,a2,c2)) continue;
                            if(conflict(b,c,b2,c2)) continue;
                            if(d1[a2][b2][c2]==-1)
                            {
                                d1[a2][b2][c2]=d1[a][b][c]+1;
                                q1.push(ID(a2,b2,c2));
                                if(d2[a2][b2][c2]!=-1)      //如果反向搜已经搜到了,已经找到答案了
                                    return d1[a2][b2][c2]+d2[a2][b2][c2];
                            }

                        }
                    }
                }
            }
            while(fnum2--)
            {
                int u=q2.front();q2.pop();
                int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;
                if(a==s[0]&&b==s[1]&&c==s[2])
                    return d2[a][b][c];
                for(int i=0;i<deg[a];++i)
                {
                    int a2=G[a][i];
                    for(int j=0;j<deg[b];++j)
                    {
                        int b2=G[b][j];
                        if(conflict(a,b,a2,b2)) continue;
                        for(int k=0;k<deg[c];++k)
                        {
                            int c2=G[c][k];
                            if(conflict(a,c,a2,c2)) continue;
                            if(conflict(b,c,b2,c2)) continue;
                            if(d2[a2][b2][c2]==-1)
                            {
                                d2[a2][b2][c2]=d2[a][b][c]+1;
                                q2.push(ID(a2,b2,c2));
                                if(d1[a2][b2][c2]!=-1)
                                    return d1[a2][b2][c2]+d2[a2][b2][c2];
                            }
                        }
                    }
                }
            }
        }
    }
    int main()
    {
        while(scanf("%d%d%d\n",&w,&h,&n)==3&&n)
        {
            int cnt=0;
            int x[M],y[M];
            int id[N][N];

            //我连读入都不会了qwq,cin读不进来空格
            for(int i=0;i<h;++i)
            {
                for(int j=0;j<w;++j)
                    a[i][j]=getchar();
                getchar();
            }
             for(int i=0;i<h;++i)               //把可以走的点打标签
                for(int j=0;j<w;++j)
                {
                    if(a[i][j]!='#')
                    {
                        x[cnt]=i;
                        y[cnt]=j;
                        id[i][j]=cnt;
                        if(islower(a[i][j]))
                            s[a[i][j]-'a']=cnt;     //把鬼也打上0 1 2的标签
                        else if(isupper(a[i][j]))
                            t[a[i][j]-'A']=cnt;
                        cnt++;
                    }
                }
            for(int i=0;i<cnt;++i)//对于每个可以走的节点
            {
                deg[i]=0;             //初始化
                for(int dir=0;dir<5;++dir)
                {
                    int nx=x[i]+dx[dir],ny=y[i]+dy[dir];
                    if(a[nx][ny]!='#')
                        G[i][deg[i]++]=id[nx][ny];          //G存的是到了一个位置能走的节点数
                }
            }
            if(n<=2)
            {
                deg[cnt]=1; G[cnt][0]=cnt; s[2]=t[2]=cnt++;   //差点忘记cnt要++
            }
            if(n<=1)
            {
                deg[cnt]=1; G[cnt][0]=cnt; s[1]=t[1]=cnt++;
            }
            memset(d1,-1,sizeof(d1));
            memset(d2,-1,sizeof(d2));
            printf("%d\n",bfs());
        }
    }
    /*
    5 5 2
    #####
    #A#B#
    #   #
    #b#a#
    #####
    16 4 3
    ################
    ## ########## ##
    #    ABCcba    #
    ################
    16 16 3
    ################
    ### ##    #   ##
    ##  #  ##   # c#
    #  ## ########b#
    # ##  # #   #  #
    #  # ##   # # ##
    ##  a#  # # #  #
    ### ## #### ## #
    ##   #   #  #  #
    #  ##### # ## ##
    ####   #B# #   #
    ##  C#   #   ###
    #  # # ####### #
    # ######  A##  #
    #        #    ##
    ################
    0 0 0
    */

普通bfs版本

//存储数据的时候,存了一个点的横纵坐标和编号
//脑子不清醒最后一个样例没过
//860ms
#include<iostream>
#include<string.h>
#include<string>
#include<queue>
#include<cstdio>
using namespace std;
int w,h,n;
const int N=20;
int id[N][N];
const int M=200;
int dx[5]={1,-1,0,0,0};
int dy[5]={0,0,1,-1,0};
int s[3],t[3];

int G[M][5];
int deg[M];
int d[M][M][M];
char a[N][N];
int ID(int a,int b,int c)
{
    return (a<<16)|(b<<8)|c;
}
bool conflict(int a1,int b1,int a2,int b2)
{
    return a2==b2||(b1==a2)&&(a1==b2);      //如果两鬼占用同一位置,或者两鬼在一步之内交换了位置,不合法
}
//代码死了两次,一次是读入,没有注意到在读fgets时需要读空格,其次是在给虚拟节点赋值的时候把个数赋了0,应该是1 答案return -1方便了检查代码
int bfs()
{
    queue<int>q;
    memset(d,-1,sizeof(d));
    //将三个状态映射为一个值方便入队,这是什么魔鬼操作
   // cout<<ID(s[0],s[1],s[2]);
    q.push(ID(s[0],s[1],s[2]));
    d[s[0]][s[1]][s[2]]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;    //c为ID的低8位,
        //cout<<a<<" "<<b<<" "<<c<<endl;
        if(a==t[0]&&b==t[1]&&c==t[2]) return d[a][b][c];
        //cout<<deg[a]<<" "<<deg[b]<<" "<<deg[c]<<endl;
        for(int i=0;i<deg[a];++i)
        {
            int a2=G[a][i];
            for(int j=0;j<deg[b];++j)
            {
                int b2=G[b][j];
                if(conflict(a,b,a2,b2)) continue;
                for(int k=0;k<deg[c];++k)
                {
                    int c2=G[c][k];
                    if(conflict(a,c,a2,c2)) continue;
                    if(conflict(b,c,b2,c2)) continue;
                    if(d[a2][b2][c2]!=-1) continue;

                    q.push(ID(a2,b2,c2));
                    d[a2][b2][c2]=d[a][b][c]+1;

                }
            }

        }
    }
    return -1;

}
int main()
{
    while(~scanf("%d%d%d\n",&w,&h,&n)&&n)  //把上一行的空格给读了 一定要先试试读入对不对 哭辽
    {

        //memset(deg,0,sizeof(deg));
//        for(int i=0;i<h;++i)
//            for(int j=0;j<w;++j)
//            cin>>a[i][j];
        for(int i=0;i<h;++i)
            fgets(a[i],20,stdin);
        //cout<<"?"<<endl;
        int x[M],y[M];
        int cnt=0;


        for(int i=0;i<h;++i)
            for(int j=0;j<w;++j)
        {
            if(a[i][j]!='#')    //记录每个合法节点的横纵坐标和编号  为了后面记录每个节点有多少种走法以及它的相邻节点
            {
                x[cnt]=i;
                y[cnt]=j;
                id[i][j]=cnt;
                if(islower(a[i][j]))
                    s[a[i][j]-'a']=cnt;
                else if(isupper(a[i][j]))
                    t[a[i][j]-'A']=cnt;
                ++cnt;

            }
        }

        for(int i=0;i<cnt;++i)
        {
            deg[i]=0;
            for(int dir=0;dir<5;++dir)
            {
                int nx=x[i]+dx[dir],ny=y[i]+dy[dir];
                if(a[nx][ny]!='#')
                    G[i][deg[i]++]=id[nx][ny];
            }
        }
        if(n<=2) {deg[cnt]=1;G[cnt][0]=cnt;s[2]=t[2]=cnt++;}
        if(n<=1) {deg[cnt]=1;G[cnt][0]=cnt;s[1]=t[1]=cnt++;}
         printf("%d\n",bfs());

    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/iroy33/article/details/84193733
BFS