AcWing 172. 立體推箱子(bfs)

原題鏈接:https://www.acwing.com/problem/content/174/
【題目大意】
立體推箱子是一個風靡世界的小遊戲。
在這裏插入圖片描述
遊戲地圖是一個N行M列的矩陣,每個位置可能是硬地(用”.”表示)、易碎地面(用”E”表示)、禁地(用”#”表示)、起點(用”X”表示)或終點(用”O”表示)。
你的任務是操作一個1×1×2的長方體。
這個長方體在地面上有兩種放置形式,“立”在地面上(1×1的面接觸地面)或者“躺”在地面上(1×2的面接觸地面)。
在每一步操作中,可以按上下左右四個鍵之一。
按下按鍵之後,長方體向對應的方向沿着棱滾動90度。
任意時刻,長方體不能有任何部位接觸禁地,並且不能立在易碎地面上。
字符”X”標識長方體的起始位置,地圖上可能有一個”X”或者兩個相鄰的”X”。
地圖上唯一的一個字符”O”標識目標位置。
求把長方體移動到目標位置(即立在”O”上)所需要的最少步數。
在移動過程中,”X”和”O”標識的位置都可以看作是硬地被利用。
【輸入格式】
輸入包含多組測試用例。
對於每個測試用例,第一行包括兩個整數N和M。
接下來N行用來描述地圖,每行包括M個字符,每個字符表示一塊地面的具體狀態。
當輸入用例N=0,M=0時,表示輸入終止,且該用例無需考慮。
【輸出格式】
每個用例輸出一個整數表示所需的最少步數,如果無解則輸出”Impossible”。
每個結果佔一行。
【數據範圍】
3≤N,M≤500
輸入樣例:
7 7
#######
#…X###
#…##O#
#…E#
#…E#
#…#
#######
0 0
【輸出樣例】:
10
分析:
本題知道箱子的起點,知道終點,爬的是NM的格子,妥妥的走迷宮的模式,但是它比走迷宮麻煩,雖同爲上下左右走,但是這個12的箱子可以上下左右走後或躺着或立着,那麼在記錄時,除了需要記錄當前所在的位置外,還需要記錄它是躺着還是立着,而且躺着還需要知道是橫躺着還是豎躺着。當躺着的時候,有兩個面與地面接觸,如何記錄呢?
在這裏我們約定當橫躺着時,記錄左半部分的位置(x,y),當豎躺着時,記錄上半部分的位置(x,y)。在記錄步驟時用數組step[x][y][lie]記錄從起點到目前(x,y)位置時狀態是lie的情況下的最少步數。
執行寬度優先搜索即可。

#include<bits/stdc++.h>
using namespace std;
struct sta{int x,y,lie;};
//lie 0表示立着,1表示橫躺着,2表示豎躺着。 
queue<sta>q;
int n,m,step[506][506][3];
const int dx[]= {0,0,-1,1},dy[]={-1,1,0,0};
sta st,ed;
char mp[506][506];
const int next_x[3][4] = {{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}};
const int next_y[3][4] = {{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
const int next_lie[3][4] = {{1,1,2,2},{0,0,1,1},{2,2,0,0}};

void pre_st_ed(){   //預處理起點和終點的狀態和位置。 
	for(int i = 1;i<= n; i++){
		for(int j = 1; j<= m; j++){
			if(mp[i][j] == 'O'){
				mp[i][j] = '.'; ed = (sta){i,j,0};
			}
			else if(mp[i][j] == 'X'){
				for(int k = 0; k< 4;k++){
					int x = i + dx[k], y = j + dy[k];
					if(x>=1 && x<= n && y >= 1 && y<= m && mp[x][y] == 'X'){
						st.x = min(i,x);st.y = min(y,j);
						if(k > 2) st.lie = 2;
						else st.lie = 1;
						mp[i][j] = '.';mp[x][y] = '.';
						break;
					}					
				}
				if(mp[i][j] == 'X'){
					st = (sta){i,j,0};
				}
			} 
		}
	}
}
bool check_valid(int x,int y){//檢查是否在格子有效範圍內。
	if(x>= 1 && x<= n && y>= 1 && y<= m) return true;
	else return false;
} 
bool valid(sta next){  //檢查擴展後的位置的有效性
	int x = next.x, y = next.y , lie = next.lie;
	if(!check_valid(next.x , next.y)) return false;
	if(mp[x][y] == '#') return false;
	if(lie == 0 && mp[x][y] == 'E') return false;
	if(lie == 1 && mp[x][y+1] == '#') return false;
	if(lie == 2 && mp[x+1][y] == '#') return false;
	return true;	
}

int bfs(){//典型寬搜框架。
	memset(step,-1,sizeof(step));
	while(!q.empty()) q.pop();
	q.push(st);
	step[st.x][st.y][st.lie] = 0;
	while(!q.empty()){
		sta head = q.front();
		q.pop();
		if(head.x == ed.x && head.y == ed.y && head.lie == ed.lie){
			return step[ed.x][ed.y][ed.lie];
		}
		for(int i = 0; i< 4; i++){
			sta nxt ;
			nxt.x = head.x + next_x[head.lie][i];
			nxt.y = head.y + next_y[head.lie][i];
			nxt.lie =next_lie[head.lie][i];
			if(valid(nxt) ==  false ) continue;
			if(step[nxt.x][nxt.y][nxt.lie] == -1){
				step[nxt.x][nxt.y][nxt.lie] = step[head.x][head.y][head.lie] + 1;
				q.push(nxt);
			}
		} 
	}
	return -1;
}
int  main(){
	while(cin >> n >> m && n){
		for(int i = 1;i<= n; i++) scanf("%s",mp[i]+1);
		pre_st_ed();
		int ans = bfs();
		if(ans == -1) printf("Impossible\n");
		else printf("%d\n", ans );
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章