【推箱子】

本文详细解析了一款经典的推箱子游戏算法实现,介绍了如何通过不同的搜索策略来寻找最优解,包括最少推动次数和最少移动次数的解决方案。文章提供了三种不同复杂度的实现代码及其评测信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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




´问题描述: 码头仓库是划分为n×m个格子的矩形阵列。有公共边的格子是相邻格子。当前仓库中 有的格子是空闲的;有的格子则已经堆放了沉重的货物。由于堆放的货物很重,单凭仓库管 理员的力量是无法移动的。仓库管理员有一项任务,要将一个小箱子推到指定的格子上去。 管理员可以在仓库中移动,但不能跨过已经堆放了货物的格子。管理员站在与箱子相对的空 闲格子上时,可以做一次推动,把箱子推到另一相邻的空闲格子。推箱时只能向管理员的对 面方向推。由于要推动的箱子很重,仓库管理员想尽量减少推箱子的次数。 ´编程任务: 对于给定的仓库布局,以及仓库管理员在仓库中的位置和箱子的开始位置和目标位置, 设计一个解推箱子问题的分支限界法, 计算出仓库管理员将箱子从开始位置推到目标位置所 需的最少推动次数。 ´数据输入: 由文件input.txt提供输入数据。输入文件第 1 行有 2个正整数 n和 m(1<=n,m<=100) , 表示仓库是n×m个格子的矩形阵列。接下来有 n行,每行有 m个字符,表示格子的状态。 S 表示格子上放了不可移动的沉重货物; w 表示格子空闲; M 表示仓库管理员的初始位置; P 表示箱子的初始位置; K 表示箱子的目标位置。 ´结果输出: 将计算出的最少推动次数输出到文件 output.txt。如果仓库管理员无法将箱子从开始位 置推到目标位置则输出“No solution!” 。 输入文件示例 输出文件示例 input.txt output.txt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值