Pushing Boxes 推箱子
- Description
推箱子是一款经典的小游戏,为简化问题,假设只有一个箱子。游戏在一个R行C列的由单位格子组成的区域中进行,每一步,你可以移动到相邻的四个格子中的一个,前提是那个格子是空的;或者,如果你在箱子旁边,你也可以推动箱子前进一格,当然不能推到区域外面。
初始时你在其中某个格子内,你要把箱子推到指定格子。又由于箱子很重,所以你要用尽量少的推动次数。
- Input Format
输入包含多组数据,每组数据第一行为两个正整数R和C,表示行数和列数。接下来R行,每行C个字符,描述游戏区域。用’#’表示石块,用’.’表示空的格子,你的起始位置为’S’,箱子起始位置为’B’,箱子目标位置为’T’。
输入以R=C=0结束。
- Output Format
对于第i组数据,如果不能将箱子推到指定格子,那么输出一行”Impossible.”(不含引号);否则,输出具有最少推动次数的操作序列。如果有多个,则输出具有最少移动次数的操作序列。如果还有多个,输出任意一个即可。
操作序列是一行由’N’,’S’,’E’,’W’,’n’,’s’,’e’,w’组成的字符串,大写字母表示推动操作,小写字母表示移动操作,方向依次表示北,南,东,西。
- Sample Input
1 7
SB....T
1 7
SB..#.T
7 11
###########
#T##......#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########
8 4
....
.##.
.#..
.#..
.#.B
.##S
....
###T
0 0
- Sample Output
EEEEE
Impossible.
eennwwWWWWeeeeeesswwwwwwwnNN
swwwnnnnnneeesssSSS
- Hint
【数据规模】
对于40%的数据,3<=R*C<=20;
对于100%的数据,1<=R,C<=20,且数据组数不超过3组;
- 分析
我们都很清楚如果要推动箱子必须站在箱子旁边,所以我们把箱子位置和人的位置都记下来记为一个状态,然后跑Spfa。
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Move_X[4]={0,0,1,-1},Move_Y[4]={1,-1,0,0};
struct Data{int x,y;}People,Box,Tag;
struct Info{
Data P,B;
int From,Push;
}Q[500000];
int Map[22][22],n,m,ans,Ans[50000],Dist[22][22][22][22],In[22][22][22][22];
inline bool operator == (Data a,Data b){return a.x==b.x && a.y==b.y;}
void write(int t){
memset(Ans,0,sizeof(Ans));
for (int i=t;i!=1;i=Q[i].From){
int h=Q[i].From;
if (Q[i].P.x==Q[h].P.x+1 && Q[i].P.y==Q[h].P.y){
if (Q[i].B==Q[h].B) Ans[++Ans[0]]='s';
else Ans[++Ans[0]]='S';
}
if (Q[i].P.x==Q[h].P.x-1 && Q[i].P.y==Q[h].P.y){
if (Q[i].B==Q[h].B) Ans[++Ans[0]]='n';
else Ans[++Ans[0]]='N';
}
if (Q[i].P.x==Q[h].P.x && Q[i].P.y==Q[h].P.y+1){
if (Q[i].B==Q[h].B) Ans[++Ans[0]]='e';
else Ans[++Ans[0]]='E';
}
if (Q[i].P.x==Q[h].P.x && Q[i].P.y==Q[h].P.y-1){
if (Q[i].B==Q[h].B) Ans[++Ans[0]]='w';
else Ans[++Ans[0]]='W';
}
}
for (int i=Ans[0];i;i--) printf("%c",Ans[i]);
printf("\n");
}
int main(){
freopen("4.in","r",stdin);
freopen("4.out","w",stdout);
for (scanf("%d%d\n",&n,&m);n!=0;scanf("%d%d\n",&n,&m)){
memset(Dist,127,sizeof(Dist));
memset(In,0,sizeof(In));
memset(Map,0,sizeof(Map));
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
scanf("%c",&Map[i][j]);
if (Map[i][j]=='S') People=(Data){i,j};
if (Map[i][j]=='B') Box=(Data){i,j};
if (Map[i][j]=='T') Tag=(Data){i,j};
if (Map[i][j]=='#') Map[i][j]=1;
else Map[i][j]=0;
}
scanf("\n");
}
for (int i=1;i<=n;i++) Map[i][0]=Map[i][m+1]=1;
for (int i=1;i<=m;i++) Map[0][i]=Map[n+1][i]=1;
Q[1]=(Info){People,Box,0,0};
bool p=false;
for (int h=1,t=1;h<=t;h++){
In[Q[h].P.x][Q[h].P.y][Q[h].B.x][Q[h].B.y]=0;
if (Q[h].P.x==1 && Q[h].P.y==2 && Q[h].B.x==5 && Q[h].B.y==4){
int tag=0;
}
for (int i=0;i<4;i++){
Data Move={Q[h].P.x+Move_X[i],Q[h].P.y+Move_Y[i]};
if (Map[Move.x][Move.y]==0){
if (Move==Q[h].B){
if (Map[Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]]==0){
if (Dist[Move.x][Move.y][Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]]>Q[h].Push+1){
Dist[Move.x][Move.y][Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]]=Q[h].Push+1;
if (In[Move.x][Move.y][Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]]){
int x=In[Move.x][Move.y][Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]];
Q[x].From=h;
Q[x].Push=Q[h].Push+1;
}
else{
In[Move.x][Move.y][Q[h].B.x+Move_X[i]][Q[h].B.y+Move_Y[i]]=++t;
Q[t].P=Move;
Q[t].B=(Data){Q[h].B.x+Move_X[i],Q[h].B.y+Move_Y[i]};
Q[t].From=h;
Q[t].Push=Q[h].Push+1;
}
}
}
}
else{
if (Dist[Move.x][Move.y][Q[h].B.x][Q[h].B.y]>Q[h].Push){
Dist[Move.x][Move.y][Q[h].B.x][Q[h].B.y]=Q[h].Push;
if (In[Move.x][Move.y][Q[h].B.x][Q[h].B.y]){
int x=In[Move.x][Move.y][Q[h].B.x][Q[h].B.y];
Q[x].From=h;
Q[x].Push=Q[h].Push;
}
else{
In[Move.x][Move.y][Q[h].B.x][Q[h].B.y]=++t;
Q[t].P=Move;
Q[t].B=Q[h].B;
Q[t].From=h;
Q[t].Push=Q[h].Push;
}
}
}
}
if (Q[t].B==Tag){
if (!p || Q[t].Push<Q[ans].Push) ans=t;
p=true;
}
}
}
if (p) write(ans);
else printf("Impossible.\n");
}
fclose(stdin); fclose(stdout);
return 0;
}