c++实现的推箱子求解程序

    这个程序是在大三上学期期末之后实现的,当时手机上有一个推箱子游戏,有 几关怎么也过不去,一怒之下写了个程序,加了几个优化之后,效率还凑合。最坏情况下也可以在几秒时间内解出7*7的推箱子,一般关卡基本上1秒之内。这个 程序还有很大的优化空间的,只是写时间长了,就不想去继续优化了。

    程序简介

    采用广度优先搜索,自己实现了简单的hashMap用以存储状态

    程序从 in.txt 读入数据,第一行是一个数,表示数据有几关,下面是若干个字符矩阵,给一组输入样例


5
#########
##*@*####
##*#D**##
#*BO*O*##
#**DD*###
###*#O###
###***###
#########
#########

#########
#**OD*###
#*ODOD**#
#BDODO@*#
#*ODOD*##
#**OD**##
#########
#########
#########

#########
#**#***##
#*DOOD*##
#@DOB*###
#*DOOD*##
#**#***##
#########
#########
#########

#########
##**#####
#*@D#####
##D*#####
##*D*####
#OD**####
#OOBO####
#########
#########

#########
###****##
###DDD*##
#@*DOO*##
#*DOOO###
####**###
#########
#########
#########

#########
###**####
###D*####
#**B*@###
#**B**###
#**B*####
###B*####
###O#####
#########

#########
#****####
#*D*@####
##B**####
#*B*#####
#*B*#####
#*B*#####
#*O*#####
#########

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#define N 20000000
#define H 4000010
#define mod 4000007
using namespace std;
int dx[]= {0,1,0,-1};
int dy[]= {1,0,-1,0};

int da[4][3]=
{
    {0,1,1},
    {1,1,0},
    {0,-1,-1},
    {-1,-1,0}
};
int db[4][3]=
{
    {1,1,0},
    {0,-1,-1},
    {-1,-1,0},
    {0,1,1}
};
int da1[4][3]=
{
    {0,-1,-1},
    {1,1,0},
    {0,1,1},
    {-1,-1,0}
};
int db1[4][3]=
{
    {1,1,0},
    {0,1,1},
    {-1,-1,0},
    {0,-1,-1}
};
int border[9][9];
int bn[20];
/*
0 空白     *
1 砖       #
2 屎       O
3 蜗牛     @
4 箱子     D
5 屎加箱子 B
6 屎加蜗牛 Q
*/
int cg(char c)
{
    switch(c)
    {
    case '*':
        return 0;
    case '#':
        return 1;
    case 'O':
        return 2;
    case '@':
        return 3;
    case 'D':
        return 4;
    case 'B':
        return 5;
    case 'Q':
        return 6;
    }
}
struct board
{
    int b[5],s;
    board() {}
    bool operator==(board t)
    {
        for(int i=0; i<=4; i++)
            if(t.b[i]!=b[i])return false;
        return true;
    }
    bool encode(int bd[7][7])
    {
        int num,x,y;
        bool flag=true;
        memset(b,0,sizeof(b));
        for(int i=0; i<=6; i++)
            for(int j=0; j<=6; j++)
            {
                if(bd[i][j]==4)flag=false;
                num=i*7+j;
                x=num/10;
                y=num%10;
                b[x]|=(bd[i][j]<<(y*3));
                if(bd[i][j]==3 || bd[i][j]==6)
                {
                    s=i*7+j;
                }
            }
        return flag;
    }

    bool decode(int bd[7][7])
    {
        int x,y;
        bool flag=true;
        for(int i=0; i<=4; i++)
        {
            for(int j=0; j<=9; j++)
            {
                x=(i*10+j)/7;
                y=(i*10+j)%7;
                bd[x][y]=(b[i]>>(j*3))%8;
                if(bd[x][y]==4)flag=false;
            }
        }
        return flag;
    }
};
int tim,sum;
struct hashmap
{
    board d[N];
    int head[H],next[N],cnt;

    void init()
    {
        memset(head,-1,sizeof(head));
        cnt=0;
    }
    int hashcode(board t)
    {
        int h=0;
        for(int i=0; i<=4; i++)
        {
            h=(h>>2)^(h<<2)^t.b[i];
        }
        h%=mod;
        if(h<0)h+=mod;
        return h;
    }
    int find(board t)
    {
        int h=hashcode(t);

        for(int i=head[h]; i!=-1; i=next[i])
        {
            if(d[i]==t)return i;
        }
        return -1;
    }
    int push(board t)
    {
        int h=hashcode(t);
        //int num=0;
        tim++;
        for(int i=head[h]; i!=-1; i=next[i])
        {
            sum++;
            if(d[i]==t)return -1;
            //  num++;
        }
        // cout<<num<<endl;
        sum++;
        d[cnt]=t;
        next[cnt]=head[h];
        head[h]=cnt++;
        return cnt-1;
    }
} hm;
int que[N],pr[N];

bool check(int x,int y,int d,int bd[7][7])
{
    if(x>=7 || x<=-1 || y>=7 || y<=-1)return false;
    int tx,ty,a,b;
    if(bd[x][y]==4 || bd[x][y]==5)//如果是个箱子
    {
        tx=x+dx[d];
        ty=y+dy[d];
        if(tx>=7 || tx<=-1 || ty>=7 || ty<=-1)return false;
        if(bd[tx][ty]!=0 && bd[tx][ty]!=2)return false;//再下一个位置一定得是空的

        if(border[tx+1][ty+1]==1)return false;

        int B=bd[tx][ty]==2,A=1;//四个箱子不能在一起,除非有四个屎
        for(int i=0; i<3; i++)
        {
            a=tx+da[d][i];
            b=ty+db[d][i];
            if(a>=7 || a<=-1 || b>=7 || b<=-1)A++;
            else if(bd[a][b]==4 || bd[a][b]==1)A++;
            else if(bd[a][b]==5)A++,B++;
        }
        if(A==4 && B!=4)return false;

        B=bd[tx][ty]==2;
        A=1;//四个箱子不能在一起,除非有四个屎
        for(int i=0; i<3; i++)
        {
            a=tx+da1[d][i];
            b=ty+db1[d][i];
            if(a>=7 || a<=-1 || b>=7 || b<=-1)A++;
            else if(bd[a][b]==4 || bd[a][b]==1)A++;
            else if(bd[a][b]==5)A++,B++;
        }
        if(A==4 && B!=4)return false;
    }
    if(bd[x][y]==1)return false;
    return true;
}
void change(int bd[7][7],int b[7][7],int sx,int sy,int x,int y,int d)
{
    memcpy(b,bd,sizeof(int)*49);
    int tx,ty;
    if(bd[x][y]==4 || bd[x][y]==5)//如果是个箱子
    {
        tx=x+dx[d];
        ty=y+dy[d];
        if(bd[tx][ty]==0)
            b[tx][ty]=4;
        else if(bd[tx][ty]==2)
            b[tx][ty]=5;
    }

    if(bd[x][y]==4)
        b[x][y]=3;
    else if(bd[x][y]==5)
        b[x][y]=6;
    else if(bd[x][y]==0)
        b[x][y]=3;
    else if(bd[x][y]==2)
        b[x][y]=6;
    if(bd[sx][sy]==3)
        b[sx][sy]=0;
    else if(bd[sx][sy]==6)
        b[sx][sy]=2;
}
int search(board t)
{
    queue<int>q;
    hm.init();
    int front=0,rear=0,tmp,bd[7][7],b[7][7],x,y,tx,ty;
    board tt;
    que[rear++]=hm.push(t);
    pr[front]=-1;
    while(front<rear)
    {
        tmp=que[front++];
        t=hm.d[tmp];
        if(t.decode(bd))return front-1;

        x=t.s/7;
        y=t.s%7;
        //cout<<x<<" "<<y<<endl;
        for(int i=0; i<=3; i++)
        {
            tx=x+dx[i];
            ty=y+dy[i];
            if(check(tx,ty,i,bd))
            {
                //if(tmp==2)cout<<'a'<<i<<endl;
                change(bd,b,x,y,tx,ty,i);
                if(tt.encode(b))
                {
                    hm.push(tt);
                    pr[rear]=front-1;
                    que[rear++]=hm.cnt-1;

                    return rear-1;
                }
                //if(tmp==0)cout<<'a'<<tt.s/7<<" "<<tt.s%7<<endl;
//
//                for(int i=0;i<=6;i++,cout<<endl)
//                    for(int j=0;j<=6;j++)
//                        cout<<b[i][j];

                if(hm.push(tt)!=-1)
                {
                    pr[rear]=front-1;
                    que[rear++]=hm.cnt-1;

                    if(rear==N)cout<<"ddd"<<endl;
                    if(rear%1000000==0)cout<<rear<<endl;
                }
            }
        }
    }
    return -1;
}

void output(int f)
{
    if(pr[f]==-1)return ;
    output(pr[f]);
    int a,b,c,d;
    a=hm.d[f].s/7;
    b=hm.d[f].s%7;
    c=hm.d[pr[f]].s/7;
    d=hm.d[pr[f]].s%7;
    if(a==c)
    {
        if(b>d)cout<<"R";
        else cout<<"L";
    }
    else
    {
        if(a>c)cout<<"D";
        else cout<<"U";
    }
}

void initialize(char bd[9][9])
{
    memset(border,0,sizeof(border));
    int x,y;
    for(int i=1; i<=7; i++)
        for(int j=1; j<=7; j++)
            if(bd[i][j]=='*' || bd[i][j]=='@')
            {
                for(int k=0; k<=3; k++)
                {
                    x=i+dx[k];
                    y=j+dy[k];
                    if(bd[x][y]=='#')
                    {
                        x=i+dx[(k+1)%4];
                        y=j+dy[(k+1)%4];
                        if(bd[x][y]=='#')border[i][j]=1;
                    }
                }
            }
//    for(int i=0; i<=8; i++,cout<<endl)
//        for(int j=0; j<=8; j++)
//            cout<<border[i][j]<<" ";
}
int main()
{
    freopen("in.txt","r",stdin);

    int num;
    scanf("%d",&num);
    while(num--)
    {
        char str[9][9];
        int bd[7][7];
        for(int i=0; i<=8; i++)
        {
            scanf("%s",str[i]);
        }
        for(int i=1; i<=7; i++)
            for(int j=1; j<=7; j++)
            {
                bd[i-1][j-1]=cg(str[i][j]);
            }
        initialize(str);
        board t;
        t.encode(bd);

        tim=sum=0;
        int tmp=search(t);
        //cout<<tmp<<endl;
        output(tmp);
        cout<<endl<<endl;
        cout<<sum*1.0/tim;
    }

    return 0;
}


留言: