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的广搜,用四维记录人的位置以及箱子的位置(我是把四维压缩在一个二十一进制数里),在用一个d记录当前状态下最少推动步数,对于一个状态,如果从上一个状态转移过来的d小于当前的d则把它的d更新,并判断这个状态是否已在队列里,如果不在就把它重新入队
(这是很慢的做法,但是复杂度算一下还是能过)
详见代码
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int i,j,k,l,m,n,x,y,h,t,pd;
int q[300000],d[300000],f[300000],fa[300000],a[30][30],num[6],v[6],u[6],movx[]={0,-1,0,1},movy[]={-1,0,1,0};
char c[]={'w','n','e','s'},s[]={'W','N','E','S'};
void work(int k)
{
int i,j;
if (fa[k]==0) return;
work(fa[k]);
for (j=4,i=fa[k];i;i/=21) u[j--]=i%21;
for (j=4,i=k;i;i/=21) v[j--]=i%21;
for (i=0;i<4;i++)
if (u[3]==v[3]&&u[4]==v[4]&&u[1]+movx[i]==v[1]&&u[2]+movy[i]==v[2]) printf("%c",c[i]);
else if (u[3]+movx[i]==v[3]&&u[4]+movy[i]==v[4]) printf("%c",s[i]);
}
int main()
{
// freopen("1734.in","r",stdin);
for (scanf("%d %d\n",&n,&m);n!=0&&m!=0;scanf("%d %d\n",&n,&m))
{
memset(f,0,sizeof f);memset(q,0,sizeof q);memset(d,0x3f,sizeof d); memset(fa,0,sizeof fa);
for (i=0;i<=n+1;i++) a[i][0]=a[i][m+1]='#';
for (i=0;i<=m+1;i++) a[0][i]=a[n+1][i]='#';
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
{
a[i][j]=getchar();
if (a[i][j]=='S') num[1]=i,num[2]=j,a[i][j]='.';
if (a[i][j]=='B') num[3]=i,num[4]=j,a[i][j]='.';
if (a[i][j]=='T') x=i,y=j,a[i][j]='.';
}
scanf("\n");
}
pd=0;
for (i=1;i<=4;i++) q[1]=q[1]*21+num[i];f[q[1]]=1;d[q[1]]=0;
for (h=0,t=1;h<t;)
{
h++;i=q[h];j=4;for (;i;i/=21) u[j--]=i%21;f[q[h]]=0;
if (u[3]==x&&u[4]==y)
{
if (d[q[h]]<d[pd]) pd=q[h];continue;
}
//if (u[1]==4&&u[2]==4&&u[3]==5&&u[4]==4)
// work(h),printf("\n");
for (i=0;i<4;i++)
if (a[u[1]+movx[i]][u[2]+movy[i]]!='#'&&(u[1]+movx[i]!=u[3]||u[2]+movy[i]!=u[4]))
{
v[1]=u[1]+movx[i];v[2]=u[2]+movy[i];v[3]=u[3];v[4]=u[4];
for (j=0,k=1;k<=4;k++) j=j*21+v[k];
if (d[q[h]]<d[j])
{
d[j]=d[q[h]],fa[j]=q[h];
if (f[j]==1) continue;
q[++t]=j;f[j]=1;
}
}
else
if (u[1]+movx[i]==u[3]&&u[2]+movy[i]==u[4])
{
v[1]=u[1]+movx[i];v[2]=u[2]+movy[i];v[3]=u[3]+movx[i];v[4]=u[4]+movy[i];
if (a[v[3]][v[4]]=='#')continue;
for (j=0,k=1;k<=4;k++) j=j*21+v[k];
if (d[q[h]]+1<d[j])
{
d[j]=d[q[h]]+1,fa[j]=q[h];
if (f[j]==1) continue;
q[++t]=j;f[j]=1;
}
}
}
if (pd==0) printf("Impossible.");else work(pd);printf("\n");
}
}
这里还有一个更优的做法(转载):如果人要推箱子,那么必须移动到箱子旁边,并且,人不会无缘无故的移动。那么,人的移动始终是围绕着箱子的。也就是说我们可以这样设计状态:用(x,y,d)描述一个状态,其中(x,y)表示箱子所在的坐标,d表示人在箱子相邻的哪一边,标程中用0,1,2,3分别表示在箱子的上,右,左,下。那么,对于一个状态(x,y,d),接下来人要推动箱子一步,人必须从箱子的d方向走到箱子的k方向,并且必须走最短路(使移动次数最少),然后再往3-k方向推动一步,就能转移到下一个状态(x’,y’,k)了。显然,这样的状态设计和转移能首先保证最少推动次数,然后能保证最少移动次数。
转载了战神的代码(不知道她知道了会不会揍我233)
#include <algorithm>
#include <cstring>
#include <cstdio>
const int fx[4]={1,0,-1,0},fy[4]={0,1,0,-1},inf=1000000000;
const char cc[4]={'S','E','N','W'};
int fr[22][22][4],f[22][22][4][2],g[22][22][4][4],d[22][22],
a[22][22],dx[500],dy[500],n,m,len,sx,sy,bx,by,tx,ty;
bool u[22][22][4];
char ans[1000000];
struct P{
int x,y,z;
P(){}
P(int a,int b,int c){x=a;y=b;z=c;}
}q[2000];
bool check(int x,int y){
return x<1 || x>n || y<1 || y>m || a[x][y];
}
int bfs(int Sx,int Sy,int xx,int yy,int k){
int Tx=xx+fx[k],Ty=yy+fy[k];
if (check(xx,yy) || check(Sx,Sy) || check(Tx,Ty)) return inf;
if (Sx==Tx && Sy==Ty) return 0;
memset(d,127,sizeof d);
a[xx][yy]=1;
d[dx[1]=Sx][dy[1]=Sy]=0;
int s=1,t=1;
for (;s<=t;s++)
for (int i=0;i<4;i++){
int x=dx[s]+fx[i],y=dy[s]+fy[i];
if (!check(x,y) && d[x][y]>d[dx[s]][dy[s]]+1){
t++;
d[dx[t]=x][dy[t]=y]=d[dx[s]][dy[s]]+1;
if (x==Tx && y==Ty){
a[xx][yy]=0;
return d[x][y];
}
}
}
a[xx][yy]=0;
return inf;
}
void find(int Sx,int Sy,int xx,int yy,int k){
bfs(Sx,Sy,xx,yy,k);
xx+=fx[k];yy+=fy[k];
for (;xx!=Sx || yy!=Sy;){
int i;
for (i=0;i<4;i++){
int x=xx-fx[i],y=yy-fy[i];
if (!check(x,y) && d[x][y]+1==d[xx][yy]) break;
}
xx-=fx[i];yy-=fy[i];
ans[++len]=cc[i]+32;
}
}
void spfa(){
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0;k<4;k++) f[i][j][k][0]=f[i][j][k][1]=inf;
memset(u,0,sizeof u);
int s=0,t=0;
for (int i=0;i<4;i++){
int dist=bfs(sx,sy,bx,by,i);
if (dist>=inf) continue;
q[++t]=P(bx,by,i);
fr[bx][by][i]=-2;
u[bx][by][i]=1;
f[bx][by][i][0]=0;
f[bx][by][i][1]=dist;
}
for (;s!=t;){
if (++s==1601) s=1;
int x=q[s].x,y=q[s].y,z=q[s].z,i=(z+2)%4;
int xx=x+fx[i],yy=y+fy[i];
u[x][y][z]=0;
if (!check(xx,yy) && (f[x][y][z][0]+1<f[xx][yy][z][0] ||
(f[x][y][z][0]+1==f[xx][yy][z][0] && f[x][y][z][1]<f[xx][yy][z][1]))){
f[xx][yy][z][0]=f[x][y][z][0]+1;
f[xx][yy][z][1]=f[x][y][z][1];
fr[xx][yy][z]=-1;
if (!u[xx][yy][z]){
if (++t==1601) t=1;
u[xx][yy][z]=1;
q[t]=P(xx,yy,z);
}
}
for (i=0;i<4;i++)
if (i!=z){
if (!check(x+fx[i],y+fy[i]) && g[x][y][z][i]<inf && (f[x][y][z][0]<f[x][y][i][0] ||
(f[x][y][z][0]==f[x][y][i][0] && f[x][y][z][1]+g[x][y][z][i]<f[x][y][i][1]))){
f[x][y][i][0]=f[x][y][z][0];
f[x][y][i][1]=f[x][y][z][1]+g[x][y][z][i];
fr[x][y][i]=z;
if (!u[x][y][i]){
if (++t==1601) t=1;
u[x][y][i]=1;
q[t]=P(x,y,i);
}
}
}
}
}
int main(){
for (;~scanf("%d%d\n",&n,&m);){
if (!n && !m) break;
for (int i=1;i<=n;i++,scanf("\n"))
for (int j=1;j<=m;j++){
char ch;
scanf("%c",&ch);
a[i][j]=(ch=='#');
if (ch=='S') sx=i,sy=j;
else if (ch=='B') bx=i,by=j;
else if (ch=='T') tx=i,ty=j;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0;k<4;k++)
for (int l=0;l<4;l++)
g[i][j][k][l]=bfs(i+fx[k],j+fy[k],i,j,l);
spfa();
int d1=0;
for (int i=0;i<4;i++)
if (f[tx][ty][i][0]<f[tx][ty][d1][0] ||
(f[tx][ty][i][0]==f[tx][ty][d1][0] && f[tx][ty][i][1]<f[tx][ty][d1][1])) d1=i;
if (f[tx][ty][d1][0]>=inf) printf("Impossible.\n");
else{
len=0;
for (;tx!=bx || ty!=by;){
if (fr[tx][ty][d1]==-1){
ans[++len]=cc[(d1+2)%4];
tx+=fx[d1];ty+=fy[d1];
}
else{
int z=fr[tx][ty][d1];
find(tx+fx[z],ty+fy[z],tx,ty,d1);
d1=z;
}
}
find(sx,sy,bx,by,d1);
for (int i=len;i>=1;i--) printf("%c",ans[i]);
printf("\n");
}
}
}
其实还有更快的做法
来自mywkll大神的代码(点击超链接,但愿在他博客里有写做法 好吧他写了 但不一定看得懂233)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<queue>
#include<cassert>
#include<climits>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define RepD(i,n) for(int i=n;i>=0;i--)
#define MEM(a) memset(a,0,sizeof(a))
#define MEMI(a) memset(a,127,sizeof(a))
#define MEMi(a) memset(a,128,sizeof(a))
#define INF (2139062143)
typedef long long LL;
struct info{
int x,y;
}w,box,e;
char ch;
bool block[25][25],vis[25][25],flag,ff,qvis[25][25];
int n,m,x[405],y[405],qx[405],qy[405],last[405],dic[405],qlast[405],qdic[405],head,tail;
int gx[]={-1,1,0,0};
int gy[]={0,0,-1,1};
int ste[405],wok[405],ans[100005];
inline bool check(int sx,int sy,int ex,int ey){
if (sx==ex&&sy==ey) return 1;
int head=0,tail=1;flag=0;
qx[1]=sx;qy[1]=sy;
qvis[sx][sy]=1;
while (head<tail&&!flag){
head++;
Rep (i,4){
int nx=qx[head]+gx[i];
int ny=qy[head]+gy[i];
if (nx&&ny&&nx<=n&&ny<=m&&!block[nx][ny]&&!qvis[nx][ny]){
qvis[nx][ny]=1;
tail++;
qx[tail]=nx;
qy[tail]=ny;
}
if (nx==ex&&ny==ey){
flag=1;break;
}
}
}
if (!flag) return 0;
return 1;
}
inline bool bfs(){
MEM(vis);
head=0;tail=1;
vis[box.x][box.y]=1;
x[1]=box.x;y[1]=box.y;last[1]=0;dic[1]=0;
while (head<tail){
head++;
Rep (i,4){
int nx=x[head]+gx[i],fx=x[head]-gx[i];
int ny=y[head]+gy[i],fy=y[head]-gy[i];
if (nx==8){
head=head;
}
if (nx&&ny&&fx&&fy&&nx<=n&&ny<=m&&fx<=n&&fy<=m&&!block[fx][fy]&&!block[nx][ny]&&!vis[nx][ny]){
MEM(qvis);
qvis[x[head]][y[head]]=1;
if (last[head]!=0&&!check(x[last[head]],y[last[head]],fx,fy)) continue;
vis[nx][ny]=1;
++tail;
x[tail]=nx;y[tail]=ny;
last[tail]=head;
dic[tail]=i;
if (nx==e.x&&ny==e.y){
return 1;
}
}
}
}
}
inline bool bfs1(int sx,int sy,int ex,int ey){
if (sx==ex&&sy==ey) return 1;
int head=0,tail=1;flag=0;
qx[1]=sx;qy[1]=sy;
vis[sx][sy]=1;
qdic[1]=qlast[1]=0;
while (head<tail&&!flag){
head++;
Rep (i,4){
int nx=qx[head]+gx[i];
int ny=qy[head]+gy[i];
if (nx&&ny&&nx<=n&&ny<=m&&!block[nx][ny]&&!vis[nx][ny]){
vis[nx][ny]=1;
tail++;
qx[tail]=nx;
qy[tail]=ny;
qdic[tail]=i;
qlast[tail]=head;
if (nx==ex&&ny==ey){
flag=1;break;
}
}
}
}
if (!flag) return 0;
ste[0]=0;
for (int tmp=tail;tmp;tmp=qlast[tmp]){
ste[++ste[0]]=tmp;
}
for (int i=ste[0];i>=2;i--){
if (qdic[ste[i-1]]==0){
ans[++ans[0]]=1;
}else if (qdic[ste[i-1]]==1){
ans[++ans[0]]=2;
}else if (qdic[ste[i-1]]==2){
ans[++ans[0]]=3;
}else if (qdic[ste[i-1]]==3){
ans[++ans[0]]=4;
}
}
return 1;
}
int main(){
//freopen("in.txt","r",stdin);
while (1){
MEM(block);ans[0]=0;ff=0;
scanf("%d%d\n",&n,&m);
if (n==0&&m==0) return 0;
For (i,n){
For (j,m){
scanf("%c",&ch);
if (ch=='S') w.x=i,w.y=j;
if (ch=='B') box.x=i,box.y=j;
if (ch=='T') e.x=i,e.y=j;
if (ch=='#') block[i][j]=1;
}
scanf("\n");
}
if (!bfs()){
printf("Impossible.\n");
continue;
}
wok[0]=0;
for (int tmp=tail;tmp;tmp=last[tmp]){
wok[++wok[0]]=tmp;
}
if (wok[0]<2){
printf("Impossible.\n");
continue;
}
for (int i=wok[0];i>=2;i--){
if (dic[wok[i-1]]==0){
MEM(vis);vis[x[wok[i]]][y[wok[i]]]=1;
if (!bfs1(w.x,w.y,x[wok[i]]+1,y[wok[i]])) ff=1;
w.x=x[wok[i]];
w.y=y[wok[i]];
ans[++ans[0]]=-1;
}else if (dic[wok[i-1]]==1){
MEM(vis);vis[x[wok[i]]][y[wok[i]]]=1;
if (!bfs1(w.x,w.y,x[wok[i]]-1,y[wok[i]])) ff=1;
w.x=x[wok[i]];
w.y=y[wok[i]];
ans[++ans[0]]=-2;
}else if (dic[wok[i-1]]==2){
MEM(vis);vis[x[wok[i]]][y[wok[i]]]=1;
if (!bfs1(w.x,w.y,x[wok[i]],y[wok[i]]+1)) ff=1;
w.x=x[wok[i]];
w.y=y[wok[i]];
ans[++ans[0]]=-3;
}else if (dic[wok[i-1]]==3){
MEM(vis);
vis[x[wok[i]]][y[wok[i]]]=1;
if (!bfs1(w.x,w.y,x[wok[i]],y[wok[i]]-1)) ff=1;
w.x=x[wok[i]];
w.y=y[wok[i]];
ans[++ans[0]]=-4;
}
}
if (ff){
printf("Impossible.\n");
continue;
}
For (i,ans[0]){
if (ans[i]==-1) printf("N");else
if (ans[i]==-2) printf("S");else
if (ans[i]==-3) printf("W");else
if (ans[i]==-4) printf("E");else
if (ans[i]==1) printf("n");else
if (ans[i]==2) printf("s");else
if (ans[i]==3) printf("w");else
if (ans[i]==4) printf("e");
}
printf("\n");
}
}
以上三个代码除了第一个(我写的),其他的我都没看233(主要是太长了),每个代码后面都有贴上评测信息(依次是结果,分数,内存,时间,语言,代码长度)
不难看出三个代码时间和内存都依次递减,代码长度依次递增,也就是说在减少程序时间和空间的时候则会浪费了许多考试时间同时错误率也提高了许多,建议带考试的时候打比较好打又能过的程序((实惠嘛),当然平时做题的时候就随意了233