• toc {:toc}

문제

상근이는 빈 공간과 벽으로 이루어진 건물에 갇혀있다. 건물의 일부에는 불이 났고, 상근이는 출구를 향해 뛰고 있다.

매 초마다, 불은 동서남북 방향으로 인접한 빈 공간으로 퍼져나간다. 벽에는 불이 붙지 않는다. 상근이는 동서남북 인접한 칸으로 이동할 수 있으며, 1초가 걸린다. 상근이는 벽을 통과할 수 없고, 불이 옮겨진 칸 또는 이제 불이 붙으려는 칸으로 이동할 수 없다. 상근이가 있는 칸에 불이 옮겨옴과 동시에 다른 칸으로 이동할 수 있다.

빌딩의 지도가 주어졌을 때, 얼마나 빨리 빌딩을 탈출할 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스는 최대 100개이다.

각 테스트 케이스의 첫째 줄에는 빌딩 지도의 너비와 높이 w와 h가 주어진다. (1 ≤ w,h ≤ 1000)

다음 h개 줄에는 w개의 문자, 빌딩의 지도가 주어진다.

  • ’.’: 빈 공간
  • ’#’: 벽
  • ’@’: 상근이의 시작 위치
  • ’*’: 불

각 지도에 @의 개수는 하나이다.

출력

각 테스트 케이스마다 빌딩을 탈출하는데 가장 빠른 시간을 출력한다. 빌딩을 탈출할 수 없는 경우에는 “IMPOSSIBLE”을 출력한다.

출처: https://www.acmicpc.net/problem/5427

풀이

테스트 케이스를 여러 개로 사용하기 때문에 초기화를 진행해주는 것이 중요하다.

4179: 불!에서 풀었듯이 불의 BFS먼저 distance를 기록하고 불의 distance보다 상근이(1박2일 상근이가 생각나서 dog라 했다)의 distance가 더 작을 때 이동할 수 있도록 코딩했다.

4179: 불!에서는 테스트 케이스가 없어 return 0를 통해 아예 프로그램을 종료하면 됐지만 이 문제에서는 테스트 케이스를 이어나가야 했기 때문에 위 방식을 사용할 수 없었다.

⇒ 때문에 break를 사용했지만 break는 묶여있는 하나의 반복문만 중지시키기 때문에 이중 for문을 모두 중지시킬 수 없었고 이것이 틀리는 이유였다.

⇒ 이 문제는 그냥 중지시키지 않고 최솟값을 찾는 방법,

endflag를 이용하여 이중 for문을 탈출하는 방법을 이용해 해결하자.

#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define MX 10000000
 
char board[1002][1002];
int dog[1002][1002];
int fire[1002][1002];
int T, w, h;
int dx[4] = {0, -1, 0, 1};
int dy[4] = {-1, 0, 1, 0};
queue<pair<int, int>> Dq, Fq;
 
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> T;
    while(T--){
        cin >> w >> h;
        int ans = MX;
 
				// board와 fire, dog의 값을 초기화 해준다.
				// w, h범위 밖은 bfs 조건에서 거르기 때문에 초기화할 필요 없다.
        for(int i = 0; i < h; i++){
            for(int j = 0; j < w; j++){
                cin >> board[i][j];
                if(board[i][j] == '*'){
                    fire[i][j] = 0;
                    dog[i][j] = -1;
                    Fq.push({i, j});
                }
                else if(board[i][j] == '@'){
                    dog[i][j] = 0;
                    fire[i][j] = -1;
                    Dq.push({i, j});
                }
                else{
                    dog[i][j] = -1;
                    fire[i][j] = -1;
                }
            }
        }
 
        // 불의 bfs
        while(!Fq.empty()){
            auto cur = Fq.front(); Fq.pop();
            for(int dir = 0; dir < 4; dir++){
                int nx = cur.X + dx[dir];
                int ny = cur.Y + dy[dir];
                if(nx < 0 || nx >= h || ny < 0 || ny >= w) continue;
                if(board[nx][ny] == '#' || fire[nx][ny] >= 0) continue;
                fire[nx][ny] = fire[cur.X][cur.Y] + 1;
                Fq.push({nx, ny});
            }
        }
/*
        for(int i = 0; i < h ; i++){
            for(int j = 0; j < w; j++){
                cout << fire[i][j] << '\t';
            }
            cout << endl;
        }
        cout << endl;
*/
        // 상근의 bfs
        int endflag = 0;
        while(!Dq.empty()){
            auto cur = Dq.front(); Dq.pop();
            for(int dir=0; dir < 4; dir++){
                int nx = cur.X + dx[dir];
                int ny = cur.Y + dy[dir];
                if(nx < 0 || nx >= h || ny < 0 || ny >= w){
                    // ans = min(ans, dog[cur.X][cur.Y]+1);
										// break대신 min으로 가장 빠른 탈출 가능 시간을 구한다.
                    ans = dog[cur.X][cur.Y]+1;
                    endflag=1;
                    break;
                }
                if(board[nx][ny] == '#' || dog[nx][ny] >= 0) continue;
                if(fire[nx][ny] != -1 && fire[nx][ny] <= dog[cur.X][cur.Y]+1) continue;
                dog[nx][ny] = dog[cur.X][cur.Y] + 1;
                Dq.push({nx, ny});
            }
            if(endflag) break;
        }
 
/*
        for(int i = 0; i < h ; i++){
            for(int j = 0; j < w; j++){
                cout << dog[i][j] << '\t';
            }
            cout << endl;
        }
*/
 
        if(ans != MX)
            cout << ans << endl;
        else
            cout << "IMPOSSIBLE" << endl;
 
        while(!Dq.empty()){
            Dq.pop();
        }
    }
}