推箱子,广搜,最小步数并输出路径

题目链接: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;
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值