궤도

[백준] 3055번 : 탈출 본문

💻 현생/⛓ 알고리즘

[백준] 3055번 : 탈출

영이오 2021. 4. 18. 17:10

문제

 


풀이

 

이 문제를 풀고나서 다른 사람들의 소스코드를 몇 개 봤다.

근데 다들 큐를 2개 사용하거나, while문을 두번 돌리거나 하는 식으로 홍수의 확산과 고슴도치의 이동을 따로따로 관리하는 방법으로 코드를 작성하는 듯 했다. 근데 굳이 그럴 필요 없다.

 

고슴도치가 없어서 내가 좋아하는 호랑이로 대체했다.

아무튼 이렇게 홍수와 고슴도치의 초기 상태가 주어졌다고 하자.

 

큐에 홍수의 위치 정보를 먼저 넣고 고슴도치의 위치 정보를 넣었다.

 

큐의 맨 앞에 있는 파도가 빠지고 새로운 파도의 위치 정보가 큐에 삽입된다.

 

이런식으로 하면 큐 하나로 홍수와 고슴도치를 관리할 수 있다.

 

한편,

이렇게 이전에 고슴도치가 방문한 곳이 홍수에 잠길 수도 있다.

그렇기 때문에 고슴도치의 visited는 다른 배열에 저장해서 관리해야 한다.

 

홍수의 visited와 고슴도치의 visited만 분리하면 하나의 큐와 하나의 while문으로 문제를 해결할 수 있다.


소스코드

 

#include <iostream>
#include <string>
#include <queue>
#include <utility>

using namespace std;

int R, C;
char map[50][50]; //지도
int dist[50][50]; //고슴도치의 이동
pair<int, int> dir[4] = {{-1, 0},  //상
                         {1,  0},  //하
                         {0,  -1}, //좌
                         {0,  1}}; //우

int bfs(pair<int, int> start, pair<int, int> end, vector<pair<int, int>> water) {
    queue<pair<int, int>> q;

    for (int i = 0; i < water.size(); i++) //물 정보 먼저 넣고
        q.push(water[i]);
    q.push(start); //고슴도치 정보 투입
    dist[start.first][start.second] = 1; //시작점 visited 체크
    while (!q.empty()) {
        int row = q.front().first;
        int col = q.front().second;
        if (dist[end.first][end.second]) //도착점의 dist 값이 0이 아니면
            return dist[end.first][end.second] - 1;
        q.pop();

        for (int i = 0; i < 4; i++) {
            int nr = row + dir[i].first;
            int nc = col + dir[i].second;
            if ((nr >= 0) && (nr < R) && (nc >= 0) && (nc < C)) { //범위 확인
                if ((dist[row][col] != 0) && (dist[nr][nc] == 0) &&
                    ((map[nr][nc] == '.') || (map[nr][nc] == 'D'))) { //고슴도치 이동
                    dist[nr][nc] = dist[row][col] + 1;
                    q.push(make_pair(nr, nc));
                } else if ((map[row][col] == '*') && (map[nr][nc] == '.')) { //홍수 이동
                    map[nr][nc] = '*';
                    q.push(make_pair(nr, nc));
                }
            }
        }
    }
    return 0;
}

int main() {
    string tmp;
    pair<int, int> start, end;
    vector<pair<int, int>> water;

    cin >> R >> C;
    for (int i = 0; i < R; i++) {
        cin >> tmp;
        for (int j = 0; j < C; j++) {
            map[i][j] = tmp[j];
            if (map[i][j] == 'S') //시작점
                start = make_pair(i, j);
            else if (map[i][j] == 'D') //도착점
                end = make_pair(i, j);
            else if (map[i][j] == '*') //홍수
                water.push_back(make_pair(i, j));
        }
    }
    int result = bfs(start, end, water);
    if (result == 0) //길이 없음
        cout << "KAKTUS\n";
    else //출력 가능
        cout << result;
}

전체 소스코드다.

 

char map[50][50]; //지도
int dist[50][50]; //고슴도치의 이동

지도 정보를 저장함과 동시에 홍수의 visited도 갱신할 map 2차원 배열과

고슴도치의 이동 횟수 정보를 저장함과 동시에 visited도 체크할 dist 2차원 배열이다.

 

int main() {
    string tmp;
    pair<int, int> start, end;
    vector<pair<int, int>> water;

    cin >> R >> C;
    for (int i = 0; i < R; i++) {
        cin >> tmp;
        for (int j = 0; j < C; j++) {
            map[i][j] = tmp[j];
            if (map[i][j] == 'S') //시작점
                start = make_pair(i, j);
            else if (map[i][j] == 'D') //도착점
                end = make_pair(i, j);
            else if (map[i][j] == '*') //홍수
                water.push_back(make_pair(i, j));
        }
    }
    int result = bfs(start, end, water);
    if (result == 0) //길이 없음
        cout << "KAKTUS\n";
    else //출력 가능
        cout << result;
}

입력값을 map에 저장하면서 시작점, 도착점, 홍수의 정보를 따로 저장한다.

 

int bfs(pair<int, int> start, pair<int, int> end, vector<pair<int, int>> water) {
    queue<pair<int, int>> q;

    for (int i = 0; i < water.size(); i++) //물 정보 먼저 넣고
        q.push(water[i]);
    q.push(start); //고슴도치 정보 투입
    dist[start.first][start.second] = 1; //시작점 visited 체크
    while (!q.empty()) {
        int row = q.front().first;
        int col = q.front().second;
        if (dist[end.first][end.second]) //도착점의 dist 값이 0이 아니면
            return dist[end.first][end.second] - 1;
        q.pop();

        for (int i = 0; i < 4; i++) {
            int nr = row + dir[i].first;
            int nc = col + dir[i].second;
            if ((nr >= 0) && (nr < R) && (nc >= 0) && (nc < C)) { //범위 확인
                if ((dist[row][col] != 0) && (dist[nr][nc] == 0) &&
                    ((map[nr][nc] == '.') || (map[nr][nc] == 'D'))) { //고슴도치 이동
                    dist[nr][nc] = dist[row][col] + 1;
                    q.push(make_pair(nr, nc));
                } else if ((map[row][col] == '*') && (map[nr][nc] == '.')) { //홍수 이동
                    map[nr][nc] = '*';
                    q.push(make_pair(nr, nc));
                }
            }
        }
    }
    return 0;
}

그리고 bfs 함수를 실행한다.

 

    for (int i = 0; i < water.size(); i++) //물 정보 먼저 넣고
        q.push(water[i]);
    q.push(start); //고슴도치 정보 투입
    dist[start.first][start.second] = 1; //시작점 visited 체크

홍수에 잠길 예정인 지역은 고슴도치가 갈 수 없다는 문제의 조건을 충족하고자

홍수의 정보를 먼저 큐에 넣고, 고슴도치의 정보를 넣는다.

 

고슴도치의 시작점을 visited 처리하기 위해 dist를 1로 설정한다.

 

        int row = q.front().first;
        int col = q.front().second;
        if (dist[end.first][end.second]) //도착점의 dist 값이 0이 아니면
            return dist[end.first][end.second] - 1;
        q.pop();

도착점의 dist값이 0이 아닐 때, 그니까 visited 처리가 됐을 때 리턴한다.

처음에 0의 거리로 갈 수 있는 S를 1로 초기화 했기 때문에 1을 뺀 값을 리턴한다.

 

        for (int i = 0; i < 4; i++) {
            int nr = row + dir[i].first;
            int nc = col + dir[i].second;
            if ((nr >= 0) && (nr < R) && (nc >= 0) && (nc < C)) { //범위 확인
                if ((dist[row][col] != 0) && (dist[nr][nc] == 0) &&
                    ((map[nr][nc] == '.') || (map[nr][nc] == 'D'))) { //고슴도치 이동
                    dist[nr][nc] = dist[row][col] + 1;
                    q.push(make_pair(nr, nc));
                } else if ((map[row][col] == '*') && (map[nr][nc] == '.')) { //홍수 이동
                    map[nr][nc] = '*';
                    q.push(make_pair(nr, nc));
                }
            }
        }

그리고 상하좌우 좌표를 확인하는데

 

                if ((dist[row][col] != 0) && (dist[nr][nc] == 0) &&
                    ((map[nr][nc] == '.') || (map[nr][nc] == 'D'))) { //고슴도치 이동
                    dist[nr][nc] = dist[row][col] + 1;
                    q.push(make_pair(nr, nc));
                }

dist[row][col]가 0이 아니란 것은 현재 고슴도치가 이동중이라는 것이다.

또한 dist[nr][nc]이 0이란 뜻은 unvisited라는 것이고, 이 상태에서 map[nr][nc]이 '.'(길)이거나 'D'(도착점)일 때 그 곳으로 이동할 수 있다. 고슴도치가 이동할 때는 dist 정보만 갱신한다.

 

고슴도치가 이미 지난 곳을 홍수가 지나갈 수도 있기 떄문에 dist[row][col]가 0이 아니어도 홍수일 가능성이 있긴 하다. 그치만 그 곳에서 갈 수 있는 좌표는 이미 고슴도치가 지나갔을 것이기 때문에 dist[nr][nc]이 0이 아닐 것이라 걸러낼 수 있다.

 

                else if ((map[row][col] == '*') && (map[nr][nc] == '.')) { //홍수 이동
                    map[nr][nc] = '*';
                    q.push(make_pair(nr, nc));
                }

홍수는 '*'로 표시되기 떄문에 map[row][col]가 '*'이라면 홍수가 이동중이라는 것이다.

홍수는 길로만 갈 수 있기 때문에 map[nr][nc]이 '.'일 때만 그곳으로 이동할 수 있고, 홍수가 이동할 때는 map 정보만 갱신한다.

Comments