[BOJ 2636] 치즈 (Java)

onAuspicious·2021년 12월 16일
0

Algorithm

목록 보기
19/24

문제

아래 <그림 1>과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(<그림 1>에서 네모 칸에 X친 부분)에는 치즈가 놓여 있지 않으며 치즈에는 하나 이상의 구멍이 있을 수 있다.

이 치즈를 공기 중에 놓으면 녹게 되는데 공기와 접촉된 칸은 한 시간이 지나면 녹아 없어진다. 치즈의 구멍 속에는 공기가 없지만 구멍을 둘러싼 치즈가 녹아서 구멍이 열리면 구멍 속으로 공기가 들어가게 된다. <그림 1>의 경우, 치즈의 구멍을 둘러싼 치즈는 녹지 않고 ‘c’로 표시된 부분만 한 시간 후에 녹아 없어져서 <그림 2>와 같이 된다.


다시 한 시간 후에는 <그림 2>에서 ‘c’로 표시된 부분이 녹아 없어져서 <그림 3>과 같이 된다.


<그림 3>은 원래 치즈의 두 시간 후 모양을 나타내고 있으며, 남은 조각들은 한 시간이 더 지나면 모두 녹아 없어진다. 그러므로 처음 치즈가 모두 녹아 없어지는 데는 세 시간이 걸린다. <그림 3>과 같이 치즈가 녹는 과정에서 여러 조각으로 나누어 질 수도 있다.

입력으로 사각형 모양의 판의 크기와 한 조각의 치즈가 판 위에 주어졌을 때, 공기 중에서 치즈가 모두 녹아 없어지는 데 걸리는 시간과 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 사각형 모양 판의 세로와 가로의 길이가 양의 정수로 주어진다. 세로와 가로의 길이는 최대 100이다. 판의 각 가로줄의 모양이 윗 줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. 치즈가 없는 칸은 0, 치즈가 있는 칸은 1로 주어지며 각 숫자 사이에는 빈칸이 하나씩 있다.

출력

첫째 줄에는 치즈가 모두 녹아서 없어지는 데 걸리는 시간을 출력하고, 둘째 줄에는 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 출력한다.

접근 방법

  1. 문제에서 요구하는 조건을 따라서 크게 분리해봅니다.
  • 주어진 공간에 치즈가 있는지 판단합니다. 만약, 치즈가 있다면 현재 존재하는 치즈의 개수를 저장합니다.
  • 치즈가 녹습니다.
  • 녹아서 없어진 공간에 공기가 채워집니다.
  1. 첫 번째 조건과 두 번째 조건인 "주어진 공간에 치즈가 있는지 판단, 치즈의 개수를 저장"하기 위해 모든 위치를 탐색해 치즈 개수를 반환하는 함수(getCheeseCnt())를 만들고 반환하는 값을 lastCheese 에 저장합니다.

  2. 두 번째 조건인 "치즈가 녹습니다."를 구현하기 위해 모든 위치를 탐색하며 해당 위치가 치즈인 경우 상, 하, 좌, 우에 '공기'가 있는 경우 값을 0으로 설정해 녹여줍니다. 그리고 airDeque에 위치를 추가해 다음에 호출 될 fillAir()에서 실제 공기로 바꿔줄 수 있게 해줍니다.

  3. 세 번째 조건인 "녹아서 없어진 공간에 공기를 채웁니다." 를 구현합니다. 두 번째 조건에서 녹은 구역이 생길때마다 우린 airDeque에 위치를 저장해줬습니다. 이를 실제 공기 배열인 air에 나타내기 위해 BFS로 탐색하며 공기를 채워줍니다.

  4. 위의 조건들을 치즈가 없어질때까지 시간과 마지막 치즈 개수를 기록하며 반복합니다.

소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;

public class Cheese_2636 {

    static int n, m;
    static int[][] board;
    static boolean[][] air;
    static int[] dx = new int[]{-1, 0, 1, 0};
    static int[] dy = new int[]{0, 1, 0, -1};
    static ArrayDeque<Node> airDeque = new ArrayDeque<>();

    static class Node {
        int x;
        int y;

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

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        String[] input = br.readLine().split(" ");
        n = Integer.parseInt(input[0]);
        m = Integer.parseInt(input[1]);
        board = new int[n][m];
        air = new boolean[n][m];

        for (int i = 0; i < n; i++) {
            input = br.readLine().split(" ");
            for (int j = 0; j < m; j++) {
                board[i][j] = Integer.parseInt(input[j]);
            }
        }

        // air 배열의 초기 구역 입력
        airDeque.offerLast(new Node(0, 0));
        fillAir();

        int time = 0;
        int lastCheese = 0;
        int cheese;

        // 치즈가 0개가 될 때 까지 순차적으로 진행합니다.
        // 1. lastCheese 에 현재 board에 있는 모든 치즈의 개수를 저장합니다.
        // 2. cheeseMelt() 를 호출해 치즈를 녹입니다.
        // 3. fillAir() 를 호출해 공기를 채웁니다.
        // 4. time++ 로 지나온 시간을 추가합니다.
        while ((cheese = getCheeseCnt()) > 0) {
            lastCheese = cheese;
            cheeseMelt();
            fillAir();
            time++;
        }

        sb.append(time).append('\n').append(lastCheese);
        System.out.println(sb);
        br.close();
    }

    // 치즈가 남아있는지 확인합니다. while 문의 종료 조건을 담당합니다.
    // 마지막으로 남은 치즈를 카운팅 하기 위해 int형을 반환합니다.
    public static int getCheeseCnt() {
        int cheese = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] == 1) cheese++;
            }
        }
        return cheese;
    }

    // 모든 노드들을 개별 탐색하며 치즈를 녹입니다.
    // isMelted() 함수로 치즈가 녹을 수 있는지 판단하고 녹게되면 airDeque()를 호출해 공기로 변환합니다.
    public static void cheeseMelt() {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] == 1 && isMelted(i, j)) {
                    board[i][j] = 0;
                    airDeque.offerLast(new Node(i, j));
                }
            }
        }
    }

    // 공기를 채우는 함수, airDeque에 있는 값으로 BFS를 돌려 공기를 채워간다.
    public static void fillAir() {
        while (!airDeque.isEmpty()) {
            Node now = airDeque.removeFirst();
            air[now.x][now.y] = true;

            for (int i = 0; i < 4; i++) {
                int tmpx = now.x + dx[i];
                int tmpy = now.y + dy[i];
                if (0 <= tmpx && tmpx < n && 0 <= tmpy && tmpy < m && !air[tmpx][tmpy] && board[tmpx][tmpy] == 0) {
                    air[tmpx][tmpy] = true;
                    airDeque.offerLast(new Node(tmpx, tmpy));
                }
            }
        }
    }

    // 선택된 위치의 치즈가 녹는 치즈이면 true, 녹지 않는 치즈이면 false를 반환
    public static boolean isMelted(int x, int y) {
        boolean melt = false;
        for (int i = 0; i < 4; i++) {
            int tmpx = x + dx[i];
            int tmpy = y + dy[i];
            if (air[tmpx][tmpy]) {
                melt = true;
                break;
            }
        }
        return melt;
    }
}

결과

profile
Beauty of Spring and JPA

0개의 댓글