1. 程式人生 > >『Pushing Boxes 雙重bfs』

『Pushing Boxes 雙重bfs』

led program 向上 遍歷 man compose put possible 都是

<更新提示>

<第一次更新>


<正文>

Pushing Boxes

Description

Imagine you are standing inside a two-dimensional maze composed of square cells which may or may not be filled with rock. You can move north, south, east or west one cell at a step. These moves are called walks. One of the empty cells contains a box which can be moved to an adjacent free cell by standing next to the box and then moving in the direction of the box. Such a move is called a push. The box cannot be moved in any other way than by pushing, which means that if you push it into a corner you can never get it out of the corner again.

One of the empty cells is marked as the target cell. Your job is to bring the box to the target cell by a sequence of walks and pushes. As the box is very heavy, you would like to minimize the number of pushes. Can you write a program that will work out the best such sequence?

想象一下,你站在一個二維的迷宮裏,由方形的格子組成,可能有或者沒有被巖石充滿。你可以在一個格子上向北、南、東或西移動。這些動作叫做步行。 其中一個空格子包含一個盒子,它可以通過站在盒子旁邊,然後在盒子的方向上移動,移動到相鄰的空格子。這樣的動作叫做推。這個盒子不能用任何其他方式來移動,這意味著如果你把它推到角落裏,你就再也不能把它從角落裏拿出來。

其中一個空格子被標記為目標格子。你的工作是把箱子放在目標格子中,通過一系列的行走和推動。由於箱子很重,你想把推的數量減到最少。你能寫出一個程序來找出最好的順序嗎?

技術分享圖片

Input Format

迷宮描述都包含一個包含兩個整數R和C的線段(R,C<=20R,C<=),表示迷宮的行數和列數。

下面是每個包含C字符的R行。每個字符描述迷宮中的一個單元。一個巖石的格子是由一個‘#‘表示的,一個空的格子由一個‘.‘表示。你的起始位置用“S”表示,方框的開始位置由“B”和目標格子按“T”表示。

Output Format

如果不可能把盒子帶到目標單元格,打印"Impossible."。

否則,輸出一個最小化推送次數的序列。如果有不止一個這樣的序列,那麽選擇一個最小化總移動次數(步行和推送)的序列。如果仍然有不止一個這樣的序列,任何一個都是可以接受的。

將序列打印為字符N、S、E、W、n、s、e和w的字符串,其中大寫字母代表推,小寫字母代表行走,不同字母代表南北、南、東和西的方向。

Sample Input

7 11
###########
#T##......#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########

Sample Output

eennwwWWWWeeeeeesswwwwwwwnNN

解析

這真是一到毒瘤\(bfs\),寫了兩天,然後校內\(OJ\)因為沒有\(SPJ\)所以掛了半天,最後老師推了鍋,就由我寫了\(SPJ\),真好。

正解應該是雙重\(bfs\)。由於題目要求在推箱子次數最小的情況下,人走的步數最小。所以設置狀態\((x,y,dir)\)代表箱子在\((x,y)\)位置,人在箱子\(dir\)方向。即人在\((x-dx_{dir},y-dy_{dir})\)的位置。然後對箱子進行\(bfs\),每一次枚舉一個方向\(j\),代表人從\((x-dx_{dir},y-dy_{dir})\)位置走到\((x-dx_{j},y-dy_{j})\),然後向\(j\)方向推一下箱子,將箱子推到\((x+dx_j,y+dy_j)\),即拓展到狀態\((x+dx_j,y+dy_j,j)\)

那麽怎麽將人從\((x-dx_{dir},y-dy_{dir})\)位置走到\((x-dx_{j},y-dy_{j})\)呢,這又是一個樸素\(bfs\)問題,再箱子\(bfs\)的基礎上對人執行一遍\(bfs\)即可。

題目還要求輸出方案,只要記錄每一個狀態是由哪一個狀態轉移過來的,然後得到最優解後用\(while\)循環倒序遍歷得到方案即可。

當然,還會有一些小問題,從人的起點走到箱子旁邊,顯然還要再寫一個\(bfs\),到箱子旁邊後,對於多個起始狀態,我們需要對每一個起始狀態都執行一遍從頭開始的雙重\(bfs\)算法,對於每一次執行雙重\(bfs\)算法,到達的第一個目標狀態就是最優解。

如果還有問題的話,就是再兩次箱子移動時,人如何移動的方案並沒有記錄,那麽就要求我們在得到答案是也要再寫一個\(bfs\)重新找到人移動的路徑,才能得到方案了。也就是說,算上雙重\(bfs\)找最短路徑,預處理一個\(bfs\),輸出方案一個\(bfs\),總共需要寫\(4\)\(bfs\)函數。

\(tips:\)本文算法可以達到\(0ms\)解決該問題。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define mset(name,val) memset(name,val,sizeof name)
#define filein(str) freopen(str".in","r",stdin)
#define fileout(str) freopen(str".out","w",stdout) 
const int N=100,M=100;
const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
struct state
{
    int x,y,dir;
    inline void clear(void)
    {
        x=y=dir=0;
    }
};
struct node
{
    int x,y;
    inline void clear(void)
    {
        x=y=0;
    }
};
int CASE,n,m,Map[N][M],cntst,cnted,fcost[10],d[N][M][4];
node man,box,end;
state st[10],ed[10],F[N][M][4];
string prepmove[10],move;
inline bool input(void)
{
    scanf("%d%d",&n,&m);
    if(!n&&!m)return false;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            char c=' ';
            while(isspace(c))c=getchar();
            if(c=='#')Map[i][j]=0;
            if(c=='.')Map[i][j]=1;
            if(c=='S')Map[i][j]=1,man=(node){i,j};
            if(c=='B')Map[i][j]=1,box=(node){i,j};
            if(c=='T')Map[i][j]=1,end=(node){i,j};
        }
    }
    return true;
} 
inline bool check(int x,int y)
{
    return x>=1&&y>=1&&x<=n&&y<=m;
}
inline char way(int k)
{
    switch(k)
    {
        case 0:return 'n';
        case 1:return 's';
        case 2:return 'w';
        case 3:return 'e';
    }
}
inline void Prepbfs(void)
{
    for(int i=0;i<4;i++)
    {
        int x=end.x-dx[i],y=end.y-dy[i];
        if(Map[x][y])
            ed[++cnted]=(state){end.x,end.y,i};
    }
    int dis[N][M];node f[N][M];
    mset(dis,-1);
    queue < node > q;
    q.push(man);
    dis[man.x][man.y]=0;f[man.x][man.y]=(node){0,0};
    while(!q.empty())
    {
        node temp=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int tx=temp.x+dx[i],ty=temp.y+dy[i];
            if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1)
            {
                if(tx==box.x&&ty==box.y)
                {
                    st[++cntst]=(state){tx,ty,i};
                    fcost[cntst]=dis[temp.x][temp.y];
                    int x=temp.x,y=temp.y;
                    while(f[x][y].x&&f[x][y].y)
                    {
                        for(int j=0;j<4;j++)
                            if(f[x][y].x+dx[j]==x&&f[x][y].y+dy[j]==y)
                                prepmove[cntst]+=way(j);
                        int Tx=x,Ty=y;
                        x=f[Tx][Ty].x;y=f[Tx][Ty].y;
                    }
                }
                else
                {
                    dis[tx][ty]=dis[temp.x][temp.y]+1;
                    f[tx][ty]=(node){temp.x,temp.y};    
                    q.push((node){tx,ty});
                }
            }
        }
    }
}
inline int Expand(node s,node t,node p)
{
    if(s.x==t.x&&s.y==t.y)return 0;
    int dis[N][M];
    mset(dis,-1);
    dis[s.x][s.y]=0;
    queue < node > q;
    q.push(s);
    while(!q.empty())
    {
        node temp=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int tx=temp.x+dx[i],ty=temp.y+dy[i];
            if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1&&(tx!=p.x||ty!=p.y))
            {
                dis[tx][ty]=dis[temp.x][temp.y]+1;
                if(tx==t.x&&ty==t.y)return dis[tx][ty];
                q.push((node){tx,ty});
            }
        }
    }
    return -1;
}
inline bool Arrive(state p)
{
    for(int i=1;i<=cnted;i++)
        if(ed[i].x==p.x&&ed[i].y==p.y&&ed[i].dir==p.dir)
            return true;
    return false;
}
inline string Reexband(node s,node t,node p)
{
    string res="";
    if(s.x==t.x&&s.y==t.y)return res;
    int dis[N][M];
    node f[N][M];
    mset(dis,-1);
    dis[s.x][s.y]=0;
    f[s.x][s.y]=(node){0,0};
    queue < node > q;
    q.push(s);
    while(!q.empty())
    {
        node temp=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int tx=temp.x+dx[i],ty=temp.y+dy[i];
            if(check(tx,ty)&&Map[tx][ty]&&dis[tx][ty]==-1&&(tx!=p.x||ty!=p.y))
            {
                dis[tx][ty]=dis[temp.x][temp.y]+1;
                f[tx][ty]=(node){temp.x,temp.y};
                if(tx==t.x&&ty==t.y)
                {
                    int x=tx,y=ty;
                    while(f[x][y].x&&f[x][y].y)
                    {
                        for(int j=0;j<4;j++)
                            if(f[x][y].x+dx[j]==x&&f[x][y].y+dy[j]==y)
                                res+=way(j);
                        int Tx=x,Ty=y;
                        x=f[Tx][Ty].x;y=f[Tx][Ty].y;
                    }
                    return res;
                }
                q.push((node){tx,ty});
            }
        }
    }
}
inline void find(int x,int y,int dir)
{
    string temp="";
    while(F[x][y][dir].x&&F[x][y][dir].y)
    {
        for(int i=0;i<4;i++)
        {
            if(F[x][y][dir].x+dx[i]==x&&F[x][y][dir].y+dy[i]==y)
            {
                temp+=(char)(way(i)-'a'+'A');
                int manendx=F[x][y][dir].x - dx[i];
                int manendy=F[x][y][dir].y - dy[i];
                int manstx=F[x][y][dir].x - dx[ F[x][y][dir].dir ];
                int mansty=F[x][y][dir].y - dy[ F[x][y][dir].dir ];
                temp+=Reexband( (node){manstx,mansty} , (node){manendx,manendy} , (node){F[x][y][dir].x,F[x][y][dir].y} );
                break;
            }
        }
        int Tx=x,Ty=y,Tdir=dir;
        x=F[Tx][Ty][Tdir].x;y=F[Tx][Ty][Tdir].y;dir=F[Tx][Ty][Tdir].dir; 
    }
    for(int i=1;i<=cntst;i++)
        if(st[i].x==x&&st[i].y==y&&st[i].dir==dir)
            temp+=prepmove[i];
    int cnttemp=0,cntmove=0;
    for(int i=0;i<temp.size();i++)
        if(temp[i]>='A'&&temp[i]<='Z')cnttemp++;
    for(int i=0;i<move.size();i++)
        if(move[i]>='A'&&move[i]<='Z')cntmove++;
    if(move.size()==0||(cnttemp<cntmove)||(cnttemp==cntmove&&temp.size()<move.size()))
        move=temp;
}
inline int Bfs(void)
{
    int res=-1;
    for(int i=1;i<=cntst;i++)
    {
        queue < state > q;
        mset(d,-1);
        mset(F,0);
        q.push(st[i]);
        F[st[i].x][st[i].y][st[i].dir]=(state){0,0,0};
        d[st[i].x][st[i].y][st[i].dir]=fcost[i];
        while(!q.empty())
        {
            state temp=q.front();
            q.pop();
            int x=temp.x,y=temp.y,dir=temp.dir;
            if(Arrive(temp))
            {
                find(x,y,dir);
                res=1;
                break;
            }
            for(int j=0;j<4;j++)
            {
                state t=(state){x+dx[j],y+dy[j],j};
                if(!check(t.x,t.y)||!Map[t.x][t.y]||d[t.x][t.y][t.dir]!=-1||!Map[x-dx[j]][y-dy[j]])continue;
                int spend=Expand( (node){x-dx[dir],y-dy[dir]} , (node){x-dx[j],y-dy[j]} , (node){x,y} );
                if( spend!=-1 )
                {
                    F[t.x][t.y][t.dir]=(state){x,y,dir};
                    d[t.x][t.y][t.dir]=d[x][y][dir]+spend+1;
                    q.push(t);
                }
            }
        }
    }
    return res;
}
int main(void)
{
    filein("test");
    fileout("test");
    input();
    Prepbfs();
    int ans=Bfs();
    if(ans==-1)printf("Impossible.\n");
    else
    {
        for(int i=move.size()-1;i>=0;i--)
            printf("%c",move[i]);
        puts("\n"); 
    }
    return 0;
}


<後記>

『Pushing Boxes 雙重bfs』