题目链接:http://www.tzcoder.cn/acmhome/problemdetail.do?method=showdetail&id=1346
解题思路:若把箱子当做自由移动的物体,就可以用最简单的广搜计算出最短步数。但是箱子不能自主移动,箱子若需要向左移动,那么人到箱子右边的时候才能使箱子移动。即人若能到箱子想移动的方向的反方向的位置时,箱子能移动。在箱子的每种状态下人的位置与其绑定。可以建立结构器绑定相关位置。
typedef struct node
{
char s[1000];
int bx;//当前箱子的x值
int by;//当前箱子的y值
int rx;//当前人的x值
int ry;//当前人的y值
int k;//在此状态下人已经移动了几步并将数据存到字符串s中
}node;
我们以箱子作为移动目标,人是否能到达箱子移动方向的反方向作为箱子的移动条件。在人是否能到目标位置也是用广搜来判断,所以人在目标位置时也是最短步数。这时候就是箱子移动次数和人走动或推动次数都是最少的。需要在人移动的时候记录步数,并复制上一个状态的已经移动过的步数。完整代码如下。
#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
char s[1000];
int bx;
int by;
int rx;
int ry;
int k;
}node;
node q[15000],e[15000];
char mp[50][50];
int tt,ww;
int box[50][50],bs[50][50];
int dir[][2]={0,1,0,-1,1,0,-1,0};
int t,w,n,m,kx,ky,jx,jy,boxx,boxy,flag,a=0,mn,flag2,cnt;
char f(int x,int y)//在箱子移动的广搜中是推动所以y是1 在人到目标位置的广搜中是移动所以y是0
{
if(y)
{
switch(x)
{
case 0:
return 'E';
case 1:
return 'W';
case 2:
return 'S';
case 3:
return 'N';
}
}
switch(x)
{
case 0:
return 'e';
case 1:
return 'w';
case 2:
return 's';
case 3:
return 'n';
}
}
int bfs(int x,int y,int fx,int fy,int zz,int gg)
{
/*
从x,y到fx,fy不能经过zz,gg
若返回1就是能到达
*/
memset(bs,0,sizeof(bs));
tt=1;
ww=0;
flag2=0;
e[0].rx=x;
e[0].ry=y;
e[0].k=0;
bs[x][y]=1;
cnt=-1;
if(x==fx&&y==fy)//刚开始就到了
flag2=1;
while(tt>ww)
{
if(flag2)
break;
for(int j=0;j<4;j++)
{
int ffx=e[ww].rx+dir[j][0];
int ffy=e[ww].ry+dir[j][1];
if(ffx==zz&&ffy==gg)
continue;
if(mp[ffx][ffy]!='#'&&!bs[ffx][ffy])
{
bs[ffx][ffy]=bs[e[ww].rx][e[ww].ry]+1;
e[tt].rx=ffx;
e[tt].ry=ffy;
e[tt].k=e[ww].k+1;
for(int o=1;o<=e[ww].k;o++)//复制上个状态怎么来的
e[tt].s[o]=e[ww].s[o];
e[tt].s[e[tt].k]=f(j,0)//加上这一步的
if(ffx==fx&&ffy==fy)
{
cnt=tt;
flag2=1;
break;
}
tt++;
}
}
ww++;
}
return flag2;
}
int main()
{
while(cin>>n>>m,n||m)
{
memset(box,0,sizeof(box));
memset(mp,'#',sizeof(mp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='S')
{
kx=i;
ky=j;
}
if(mp[i][j]=='B')
{
boxx=i;
boxy=j;
}
if(mp[i][j]=='T')
{
jx=i;
jy=j;
}
}
}
t=1;
w=0;
flag=0;
q[0].k=0;
q[0].rx=kx;
q[0].ry=ky;
q[0].bx=boxx;
q[0].by=boxy;
box[boxx][boxy]=1;
while(t>w)
{
if(q[w].bx==jx&&q[w].by==jy)
{
flag=1;
mn=w;
break;
}
if(flag)
break;
for(int i=0;i<4;i++)
{
int dx=q[w].bx+dir[i][0];
int dy=q[w].by+dir[i][1];
int xx=q[w].bx-dir[i][0];
int yy=q[w].by-dir[i][1];
if(mp[dx][dy]!='#'&&!box[dx][dy]&&bfs(q[w].rx,q[w].ry,xx,yy,q[w].bx,q[w].by))//如果箱子没越界并且这个位置箱子从来没来过
{ //并且反方向的位置人能到达就可以推箱子
box[dx][dy]=box[q[w].bx][q[w].by]+1;
q[t].rx=q[w].bx;
q[t].ry=q[w].by;
q[t].bx=dx;
q[t].by=dy;
q[t].k=0;
for(int j=1;j<=q[w].k;j++)
q[t].s[++q[t].k]=q[w].s[j];
if(cnt!=-1)//cnt记录到达目标点时记录人移动步数的结构体e的下标
{
for(int j=1;j<=e[cnt].k;j++)
{
q[t].s[++q[t].k]=e[cnt].s[j];//把人移动的步数复制
}
}
q[t].s[++q[t].k]=f(i,1);//加上这一步
t++;
}
}
w++;
}
cout<<"Maze #"<<++a<<endl;
if(flag)
{
for(int i=1;i<=q[mn].k;i++)
cout<<q[mn].s[i];
cout<<endl;
cout<<endl;
}
else
cout<<"Impossible."<<endl<<endl;
}
return 0;
}