알고리즘/그래프

[BOJ] 백준 2573 - 빙산 풀이

송승현(SSH) 2022. 12. 20. 23:40

1. 문제

https://www.acmicpc.net/problem/2573

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을

www.acmicpc.net

 

2. 풀이

백준 골드4에 해당하는 DFS 문제이다.
우선 입력으로 주어지는 배열의 row와 column을 통해 정수형 2차원 배열을 구성하고 다음 줄부터 주어지는 배열의 값을 2차원 배열에 값을 저장한다.

그 후 while 무한 반복을 돌면서 다음과 같은 로직을 순서대로 수행한다.
1. 1년 추가

2. 얼음 구역의 개수 체크 ( 이는 처음으로 주어지는 배열의 값이 이미 다 녹아있거나 이미 두 덩어리로 분리되어 있는 경우가 있으므로. )

3. 2번의 체크로 이미 얼음이 다 녹아있거나 두 덩어리로 분리되어 있다면 -> 반복문 탈출

4. 3번에서 반복문을 탈출하지 않았다면 else 실행

5. 얼음이 녹을 수 있는 부분 체크
얼음이 녹을 수 있는 부분을 체크하고 그 부분에서 동서남북으로 0이 몇 개가 있는지 체크한다.

처음 문제를 풀 때 0이 아닌 부분을 하나씩 체크하여 녹는 것을 처리했더니 연속해서 녹아버리는 문제가 발생했었다. 예를 들어, 문제에서 주어지는 배열 값 중 2행 2열, 2행 3열 값을 보면 2와 4이다. 1년이 지난 후 이 두 값은 0과 2가 된다. 만약 하나씩 체크하여 녹는 것을 먼저 처리해버리면 1년이 지난 후 2와 4의 값은 0과 1이 된다. 이유는 2를 먼저 처리했을 경우 0이 되버리기 때문에 4를 체크할 때 동서남북에 0이 3개가 되어 버리기 때문이다.

따라서 녹을 수 있는 점을 먼저 리스트에 저장해서 나중에 처리해야 한다.

6. 얼음이 실제 녹는 로직
리스트에 넣은 점을 하나씩 탐색하면서 얼음의 양을 체크하여 계산한다. 이 때, 현재 얼음의 양 - 체크한 0의 개수가 0보다 작다면 0을 넣고 아니라면 뺀 값을 넣는다.
그 후 리스트를 clear를 해준다.

7. 얼음 구역 다시 체크
얼음을 녹는 로직을 수행했으니 현재 얼음 구역이 몇 개 있는지 체크한다.

8. 7에서 체크한 얼음 구역이 2개 이상이라면 반복문을 빠져나간다.

마지막으로 계산한 년수를 출력해주면 정답!!!


3. 소스 코드

import java.io.*;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class Main {
    static int N, M;
    static int[][] map;
    static boolean[][] visited;

    static int[] dx = {1, -1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    static class Point {
        int x;
        int y;
        int waterCnt;

        public Point(int x, int y, int waterCnt) {
            this.x = x;
            this.y = y;
            this.waterCnt = waterCnt;
        }
    }

    static ArrayList<Point> points = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int years = 0;
        while (true) {
            years++;

            // 얼음 구역의 개수 체크
            int firstCnt = 0;
            visited = new boolean[N][M];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (map[i][j] != 0 && !visited[i][j]) {
                        IceCnt(i, j);
                        firstCnt += 1;
                    }
                }
            }

            if (firstCnt == 0 || firstCnt >= 2) {
                years = 0;
                break;
            } else {
                // 얼음이 녹을 수 있는 점을 체크하는 로직
                for (int i = 0; i < N; i++) {
                    for (int j = 0; j < M; j++) {
                        if (map[i][j] != 0) {
                            IceMalt(i, j);
                        }
                    }
                }

                // 얼음이 실제 녹는 로직
                for (int i = 0; i < points.size(); i++) {
                    if (map[points.get(i).x][points.get(i).y] - points.get(i).waterCnt < 0) {
                        map[points.get(i).x][points.get(i).y] = 0;
                    } else {
                        map[points.get(i).x][points.get(i).y] -= points.get(i).waterCnt;
                    }
                }
                points.clear();

                // 얼음 구역의 개수 체크
                visited = new boolean[N][M];
                int cnt = 0;
                for (int i = 0; i < N; i++) {
                    for (int j = 0; j < M; j++) {
                        if (map[i][j] != 0 && !visited[i][j]) {
                            IceCnt(i, j);
                            cnt += 1;
                        }
                    }
                }

                // 열음 구역의 개수가 2가 넘거나 전부 녹을 때까지 두 덩어리 이상으로 분리되지 않는다면
                if (cnt >= 2) {
                    break;
                }
            }
        }

        bw.write(Integer.toString(years));
        bw.flush();
        bw.close();
        br.close();
    }

    static void IceCnt(int x, int y) {
        visited[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int xf = x + dx[i];
            int yf = y + dy[i];

            if (xf >= 0 && xf < N && yf >= 0 && yf < M && !visited[xf][yf] && map[xf][yf] != 0) {
                IceCnt(xf, yf);
            }
        }
    }

    static void IceMalt(int x, int y) {
        int waterCntMethod = 0;
        for (int i = 0; i < 4; i++) {
            int xf = x + dx[i];
            int yf = y + dy[i];

            if (map[xf][yf] == 0) {
                waterCntMethod += 1;
            }
        }

        if (waterCntMethod > 0) {
            points.add(new Point(x, y, waterCntMethod));
        }
    }
}