凡人修c传(五)推箱子(bfs+思维)

题目链接Problem - 1254

        前段时间,本萌新做了一个推箱子的问题。当时为了写这道题,还专门玩了几关推箱子,想了一天,终于在学长的帮助下,得出了一个平民解法。。。没错,纯bfs。。。

        大体板子:

既然箱子到指定地点才算成功,那我们不妨就让箱子进行上下左右移动,看箱子到指定要走多少格。

        重点一:去重

在这道题中,去重是个不能忽视的问题,总不能让人一直跑,但是如果像往常一样仅仅按照单个箱子的坐标去重,又会少考虑很多情况。“方向”是一个很重要的判断因素,我们可以开个bool三维版去判断是否重复(一个下标是x,一个下标是y,还有一个下标用0,1,2,3代表推的四个方向)。

     重点二:人

箱子推的方向,都取决于人。例如箱子此时的坐标为(x,y),枚举到箱子向右推到(x,y+1),那此时人必须能够到达(x,y-1)才能实现这个功能。所以,就用到了第二个bfs来做判断,判断人能否到他因该到达的位置,如果不能,就跳过进行下一个枚举。

        重点三:箱子被卡死

如果玩过推箱子的话,就直到,箱子的四个相邻的格子,倘若有两个(非像上下或左右这样的对应方向)及以上都是墙,那这个箱子就可以good game了,耶稣来了都救不了,我说的,因而这种情况也要进行判断并加以考虑。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<math.h>
#include<string.h>
#include<queue>
#include<stack>
typedef long long ll;
using namespace std;
ll n,m;
ll xz,yz,x2,y2,x3,y3;
int b[20][20];
bool c[20][20][5];
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
bool r[20][20];
struct we{
	ll x,y;
	ll bu;
	ll renx;
	ll reny;
};
bool ren(we yuan,we xian){
	queue<we>zz;
	memset(r,0,sizeof(r)); 
	r[yuan.renx][yuan.reny]=1;
	zz.push({yuan});
	while(!zz.empty()){
		we emm=zz.front();
		zz.pop();
		if(emm.renx==xian.renx&&emm.reny==xian.reny){
			return 0;
		}
		for(int i=0;i<4;i++){
				int xx=emm.renx+dx[i];
				int yy=emm.reny+dy[i];
				if(xx<1||xx>n||yy<1||yy>m||r[xx][yy]==1||b[xx][yy]==1||(xx==yuan.x&&yy==yuan.y))continue;
				r[xx][yy]=1;
				zz.push({emm.x,emm.y,0,xx,yy});
		}
	}
	return 1;
}
int main(){
	ll t;
	cin>>t;
	while(t--){
		queue<we>hh;
		cin>>n>>m;
		memset(c,0,sizeof(c));
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
					cin>>b[i][j];
					if(b[i][j]==4){
						xz=i;
						yz=j;
					}
					if(b[i][j]==2){
						x2=i;
						y2=j;
					}
					if(b[i][j]==3){
						x3=i;
						y3=j;
					}
				
			}
		}
		
		hh.push({x2,y2,0,xz,yz});
		ll ans=-1;
		while(!hh.empty()){
			we now=hh.front();
			if(now.x==x3&&now.y==y3){
				ans=now.bu;
				break;
			}
			hh.pop();
			for(int i=0;i<4;i++){
				int xx=now.x+dx[i];
				int yy=now.y+dy[i];
				int rx=now.x-dx[i];
				int ry=now.y-dy[i];
				if(rx<1||rx>n||ry<1||ry>m||b[rx][ry]==1)continue;
				if(c[xx][yy][i]==1)continue;
				if(xx<1||xx>n||yy<1||yy>m||b[xx][yy]==1)continue;
				we will;
				will.x=xx;
				will.y=yy;
				will.renx=rx;
				will.reny=ry;
				will.bu=now.bu+1;
				if(ren(now,will))continue;
				c[xx][yy][i]=1;
				hh.push(will);
				
			}
		}
			cout<<ans<<"\n";
		
			
	}
}

        

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值