[BOJ] 2573 - 빙산

황규빈·2022년 2월 8일
0

알고리즘

목록 보기
22/33

💎 문제


지구 온난화로 인하여 북극의 빙산이 녹고 있다. 빙산을 그림 1과 같이 2차원 배열에 표시한다고 하자. 빙산의 각 부분별 높이 정보는 배열의 각 칸에 양의 정수로 저장된다. 빙산 이외의 바다에 해당되는 칸에는 0이 저장된다. 그림 1에서 빈칸은 모두 0으로 채워져 있다고 생각한다.

빙산의 높이는 바닷물에 많이 접해있는 부분에서 더 빨리 줄어들기 때문에, 배열에서 빙산의 각 부분에 해당되는 칸에 있는 높이는 일년마다 그 칸에 동서남북 네 방향으로 붙어있는 0이 저장된 칸의 개수만큼 줄어든다. 단, 각 칸에 저장된 높이는 0보다 더 줄어들지 않는다. 바닷물은 호수처럼 빙산에 둘러싸여 있을 수도 있다. 따라서 그림 1의 빙산은 일년후에 그림 2와 같이 변형된다.

그림 3은 그림 1의 빙산이 2년 후에 변한 모습을 보여준다. 2차원 배열에서 동서남북 방향으로 붙어있는 칸들은 서로 연결되어 있다고 말한다. 따라서 그림 2의 빙산은 한 덩어리이지만, 그림 3의 빙산은 세 덩어리로 분리되어 있다.

한 덩어리의 빙산이 주어질 때, 이 빙산이 두 덩어리 이상으로 분리되는 최초의 시간(년)을 구하는 프로그램을 작성하시오. 그림 1의 빙산에 대해서는 2가 답이다. 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다.

💎 입력


첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지막 행과 열에는 항상 0으로 채워진다.

💎 출력


첫 줄에 빙산이 분리되는 최초의 시간(년)을 출력한다. 만일 빙산이 다 녹을 때까지 분리되지 않으면 0을 출력한다.

💎 풀이 방법


장장 3시간 반을 고민해서 풀었다,,,,4번 정도 제출해서 틀렸었는데 테스트 케이스로 고민하고 코드를 한번 갈아 엎고 하느라 푸는데 많이 오래걸렸다. 문제에서 주어진대로 찬찬히 따라갔다면 충분히 테스트케이스 두번 정도 더 해서 푸는 시간을 줄일 수 있었는데, 처음부터 무작정 접근한 탓인 것 같다,,, BFS는 문제에서 주어진대로 잘 따라가서 풀 수 있도록 하자 ㅠㅠㅠ

빙산이 주어지면 생각해야하는 순서 3가지를 따라가야 했다.

1. 빙산을 발견하면 방문 표시를 해주고 queue에 넣어서 BFS탐색을 반복하기
2. 빙산의 상하좌우를 확인하여 빙산이 없는 위치 개수 만큼 빙산의 높이 줄이기
3. BFS를 완료한 후에 빙산의 덩어리 수를 확인해주기

이 순서를 반드시 생각해서 풀어줘야 했다!! 이 때 빙산의 덩어리 수를 확인하였을 때 0이 된다면 한 덩어리인채로 끝까지 녹았거나 처음부터 빙산이 없을 것이므로 이를 고려해주는 것으로 코드를 작성하였다.

while(island == 0){
        queue <pair <int, int>> q;
        for(int i = 0;i < N;i++){
            for(int j = 0;j < M;j++){
                if(ice[i][j] != 0 && !visited[i][j]){
                    q.push({i, j});
                    visited[i][j] = true;
                    BFS(q);
                    island++;
                }
            }
        }
        if(island != 1)
        {
            if(island >= 2)
                break;
            else
            {
                answer = 0;
                break;
            }
        }
        else{
            island = 0;
            answer++;
        }
        for(int i = 0;i < N;i++){
            for(int j = 0;j < M;j++){
                visited[i][j] = false;
            }
        }
    }

덩어리 수가 1일 경우에만 햇수를 증가시켜줄 수 있다. 따라서 island라는 변수를 통해 햇수가 증가함에 따른 빙산의 높이 변화를 확인해줄 것이다. queue를 통해서 빙산이 존재하는 위치의 행과 열을 넣어준 후 이를 BFS에서 활용할 것이다! 이 때 BFS가 한 번 완료될 때마다 빙산의 덩어리 수를 증가시켜준다.

BFS가 완료된 이후에는 덩어릿수를 확인하여 만약 덩어리가 1일 경우에는 햇수를 증가시켜주고 덩어리가 1이 아닌 2이상이라면 햇수 결과를 출력해주고, 덩어리가 0이라면 처음 빙산 그대로 쭉 녹은 것으로 햇수는 0으로 초기화 하여 결과를 출력하였다.

void BFS(queue<pair <int, int>> q){
    
    while(!q.empty()){
        int count = 0;
        int tempY = q.front().first;
        int tempX = q.front().second;
        q.pop();
        for(int i = 0;i < 4;i++){
            int dy = tempY + y[i];
            int dx = tempX + x[i];
            if(ice[dy][dx] == 0 && !visited[dy][dx]){
                count++;
            }
            if(ice[dy][dx] != 0 && !visited[dy][dx]){
                q.push({dy, dx});
                visited[dy][dx] = true;
            }
        }
        if(count >= ice[tempY][tempX]){
            ice[tempY][tempX] = 0;
        }
        else
            ice[tempY][tempX] -= count;
    }
    
}

BFS를 잠시 확인해보면 상하좌우의 빙산이 존재하는지 탐색하여 이에 따라 1년마다 빙산의 높이 변화를 저장해주었다. 이는 x, y 좌표를 배열을 통해 따로 저장해주고 4번 반복하여 상하좌우를 탐색하였다. 이 때 방문하지 않은 빙산인지 확인을 하여 BFS를 반복할 수 있어야한다!!

많이 헤맸던 문제였던 것 같다,,, 그래프 탐색의 대부분은 꼭 방문표시를 확인하며 푸는 문제임과 어떤 조건에서 BFS에서의 queue에 값을 넣어주는지를 다시금 명심해야하는 것을 상기시켜줄 수 있는 문제였다.

💎 전체 코드


#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int N, M;
int ice[301][301];
bool visited[301][301];
int x[4] = {1, 0, -1, 0};
int y[4] = {0, -1, 0, 1};
int answer = 0;

void BFS(queue<pair <int, int>> q){
    
    while(!q.empty()){
        int count = 0;
        int tempY = q.front().first;
        int tempX = q.front().second;
        q.pop();
        for(int i = 0;i < 4;i++){
            int dy = tempY + y[i];
            int dx = tempX + x[i];
            if(ice[dy][dx] == 0 && !visited[dy][dx]){
                count++;
            }
            if(ice[dy][dx] != 0 && !visited[dy][dx]){
                q.push({dy, dx});
                visited[dy][dx] = true;
            }
        }
        if(count >= ice[tempY][tempX]){
            ice[tempY][tempX] = 0;
        }
        else
            ice[tempY][tempX] -= count;
    }
    
}

int main(int argc, const char * argv[]) {
    
    cin >> N >> M;
    
    for(int i = 0;i < N;i++){
        for(int j = 0;j < M;j++){
            cin >> ice[i][j];
        }
    }
    
    int island = 0;
    
    while(island == 0){
        queue <pair <int, int>> q;
        for(int i = 0;i < N;i++){
            for(int j = 0;j < M;j++){
                if(ice[i][j] != 0 && !visited[i][j]){
                    q.push({i, j});
                    visited[i][j] = true;
                    BFS(q);
                    island++;
                }
            }
        }
        if(island != 1)
        {
            if(island >= 2)
                break;
            else
            {
                answer = 0;
                break;
            }
        }
        else{
            island = 0;
            answer++;
        }
        for(int i = 0;i < N;i++){
            for(int j = 0;j < M;j++){
                visited[i][j] = false;
            }
        }
    }
    
    cout << answer << "\n";
    
    return 0;
}

💎 고민


내가 푼 BFS 문제중에서도 까다로웠다,, 시간초과보다도 예외 처리에 대한 테스트케이스를 재차 실패하니 헷갈리기 시작해서 어려웠다 ㅠㅠ BFS는 그림과 문제가 길어지는 경우가 있는 것 같은데 시작하기 앞서 순서도를 세워보고 접근하는 것이 좋을 것 같다!!
어려웠지만 내 힘으로 결국 풀어내서 뿌듯하다...다음에는 푸는 시간을 좀 더 줄여보도록 하자!!

화팅

profile
안녕하세욤

0개의 댓글