原題鏈接: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;
}