3 minute read

문제 탐색하기

  • N×M의 모눈종이 위에 아주 얇은 치즈가 그림과 같이 표시되어 있다.
    • 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다.
  • 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다.
  • 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다.
  • 따라서 그림과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.

  • 모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다.
  • 치즈가 모두 녹아 없어지는 데 걸리는 시간을 구하는 프로그램을 작성하시오.

입력

  • 첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (3 ≤ N, M ≤ 100)이 주어진다.
  • 이후 N개의 줄에는 모눈종이의 각 칸에 대한 정보를 나타내는 0 이상 1 이하의 정수가 주어진다.
  • 0은 치즈가 없는 칸을 나타내고, 1은 치즈가 있는 칸을 나타낸다.

출력

  • 치즈가 모두 녹아 없어지는 데 걸리는 시간을 출력한다.

예제 입력

8 9
0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0
0 0 0 1 1 0 1 1 0
0 0 1 1 1 1 1 1 0
0 0 1 1 1 1 1 0 0
0 0 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0

예제 출력

4

코드 설계하기

구현 방법

  1. 외부 공기 영역 탐색 (BFS)
    • (0,0)에서 시작하여 BFS로 외부 공기(0)를 탐색
    • visited 배열에 외부 공기 영역을 표시
    • 이 과정으로 치즈 내부의 공기와 외부 공기를 구분
  2. 녹을 치즈 찾기 (BFS)
    • 모든 치즈(1)에 대해 주변 4방향을 확인
    • 외부 공기(visited가 true인 0)와 접촉한 면의 수를 세어 2개 이상이면 녹을 치즈로 표시
    • 녹을 치즈가 없으면 종료
  3. 치즈 녹이기
    • 녹을 치즈를 모두 찾은 후 한 번에 녹임
    • 시간 증가

시간 복잡도

  • 외부 공기 영역 탐색: O(N*M)
  • 녹을 치즈 찾기: O(N*M)
  • 치즈 녹이기: O(녹을 치즈의 수)
  • 전체 시간 복잡도: O(T * 3N * M)
    • T: 치즈가 모두 녹는데 걸리는 시간 (최대 N/2)
    • N X M: 격자의 크기 (최대 100*100)
    • 실제 최악의 경우: O(50 * 3 * 10,000) = O(1,500,000)

이므로 시간 제한 내에 충분히 수행이 가능하다.

시도 회차 별 수정사항(Optional)

  1. 첫 번째 시도
    • 한 번의 BFS로 외부 공기와 치즈를 동시에 처리하려고 시도
    • 문제점:
      • 치즈 내부의 공기와 외부 공기를 구분하지 못함
      • 치즈가 녹는 조건을 정확히 판단하지 못함
  2. 두 번째 시도 (정답)
    • 외부 공기 영역 탐색녹을 치즈 찾기분리
    • 매 시간마다 visited 배열을 새로 생성하여 외부 공기 영역을 정확히 구분
    • 치즈가 녹는 조건을 정확히 판단하여 구현

정답 코드 (Java)

package BFS;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class b2638 {
    private static int N, M, cnt = 0;
    private static boolean[][] visited;
    private static int[][] map;
    private static int[] dx = {-1, 1, 0, 0};
    private static int[] dy = {0, 0, -1, 1};


    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][M];
        visited = new boolean[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());
            }
        }

        while (true) {
            // 외부 공기 영역 표시
            boolean[][] visited = new boolean[N][M];
            Queue<int[]> airQueue = new LinkedList<>();
            airQueue.add(new int[]{0, 0});
            visited[0][0] = true;

            while (!airQueue.isEmpty()) {
                int[] current = airQueue.poll();
                int x = current[0], y = current[1];

                for (int k = 0; k < 4; k++) {
                    int nx = x + dx[k], ny = y + dy[k];
                    if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
                    if (map[nx][ny] == 0 && !visited[nx][ny]) {
                        visited[nx][ny] = true;
                        airQueue.add(new int[]{nx, ny});
                    }
                }
            }

            // 녹을 치즈 찾기
            Queue<int[]> meltQueue = new LinkedList<>();
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (map[i][j] == 1) {
                        int airCount = 0;
                        for (int k = 0; k < 4; k++) {
                            int nx = i + dx[k], ny = j + dy[k];
                            if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
                            if (map[nx][ny] == 0 && visited[nx][ny]) {
                                airCount++;
                            }
                        }
                        if (airCount >= 2) {
                            meltQueue.add(new int[]{i, j});
                        }
                    }
                }
            }

            // 녹을 치즈가 없으면 종료
            if (meltQueue.isEmpty()) break;

            // 치즈 녹이기
            while (!meltQueue.isEmpty()) {
                int[] melt = meltQueue.poll();
                map[melt[0]][melt[1]] = 0;
            }

            cnt++;
        }

        System.out.println(cnt);
    }
}

Leave a comment