AcWing 172. 立体推箱子

在这里插入图片描述

题意:

箱子是一个长方体,有立着和躺着两种形态,给定图及各点含义,问从起点到终点的最小步数

思路:

图论最小步数,选择BFS进行求解,不同于常规的搜索的那四个方向,这次是一个长方体滚来滚去,有三种不同的状态,注意一点方向数组不要写错了。其他的详见代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int n,m;
char mp[550][550];
const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
struct rec {
	int x,y,lie;
};
bool in(int x,int y) {
	if(x>0&&x<=n&&y>0&&y<=m) return true;
	return false;
} 
rec st,ed;
queue<rec>q;
int d[550][550][4];
void getSt() {
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			if(mp[i][j] == 'O')  {//找终点 
				ed.x = i;
				ed.y = j;
				ed.lie = 0;
				mp[i][j] = '.';
			} else if(mp[i][j] == 'X') {//找起点,可能有两格所以要搜一下四周 
				for(int k=0;k<4;k++) {
					int tx = i+dx[k];
					int ty = j+dy[k];
					if(in(tx,ty) && mp[tx][ty] == 'X') {//如果找到是两个格子为起点的取最小 
						st.x = min(tx,i);
						st.y = min(ty,j);
						st.lie = k<2?1:2;//当k为1或0时枚举到的时上下竖着躺的属于1,另一种属于2 
						mp[i][j] = mp[tx][ty] = '.';
						break;
					}
					if(mp[i][j] == 'X') {//如果不是两个格子的情况那么碰到的唯一一个x就是起点且是直立的属于0 
						st.x = i;
						st.y = j;
						st.lie = 0;
					}
				}
			}
		}
	}
	
}
bool check(rec Next) {//下一位置的最大的那边合不合法 
	if(!in(Next.x,Next.y)) return false;
	if(mp[Next.x][Next.y] == '#') return false;//当前位置不行直接返回 
	if(mp[Next.x][Next.y] != '.' && Next.lie == 0)return false;//直立情况当前位必须合法 
	if(mp[Next.x][Next.y+1] == '#' && Next.lie == 1) return false;//上下躺平情况下一 行需合法 
	if(mp[Next.x+1][Next.y] == '#' && Next.lie == 2) return false;// 下一列需合法 
	return true;
}
int Nextx[3][4] = {{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}};//三种不同放置情况的坐标变化 
int Nexty[3][4] = {{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
int Nextlie[3][4] = {{1,1,2,2},{0,0,1,1},{2,2,0,0}};
int bfs() {
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			for(int k=0;k<=2;k++) {
				d[i][j][k] = -1;//各点的步数初始为-1 
			}
		}
	}
	while(q.size()) q.pop(); 
	d[st.x][st.y][st.lie] = 0;//起点步数为0 
	q.push(st);//压入起点 
	while(q.size()) {//开始广搜 
		rec now = q.front();//取出队头 
		q.pop();
		for(int i=0;i<4;i++) {
			rec Next;
			Next.x = now.x + Nextx[now.lie][i];
			Next.y = now.y + Nexty[now.lie][i];
			Next.lie = Nextlie[now.lie][i];
			if(check(Next) && d[Next.x][Next.y][Next.lie] == -1) {
				d[Next.x][Next.y][Next.lie] = d[now.x][now.y][now.lie] + 1;
				q.push(Next);
				if(Next.x == ed.x && Next.y == ed.y && Next.lie == ed.lie) {
					return d[Next.x][Next.y][Next.lie];
				}
			} 
		}
	}
	return -1;
}
int main()
{
	while(cin>>n>>m &&n&&m) {
		for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
		getSt();
		int ans = bfs();
		if(ans == -1) {
			cout<<"Impossible"<<endl;
		} else {
			cout<<ans<<endl;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值