[ 백준 ] 2636 / 치즈

金弘均·2021년 9월 16일
0

Baekjoon Online Judge

목록 보기
181/228
post-thumbnail

# Appreciation

/*
 * Problem :: 2636 / 치즈
 *
 * Kind :: Simulation + BFS
 *
 * Insight
 * - 공기와 접촉한 치즈칸을 찾아야 한다
 *   + 공기가 어느칸인지 알아야 한다
 *     # 판의 가장자리는 치즈가 놓여있지 않다
 *       즉, 판의 가장자리는 무조건 공기이다
 *       -> 판의 가장자리의 아무칸부터 BFS 해서 공기칸을 모두 찾는다
 *          BFS 후, 방문된 적이 없으며 치즈칸이 아닌 칸은 구멍칸이다
 *          <- 치즈로 감싸진 빈칸(치즈가 들어있지 않은 칸)은
 *             공기가 들어갈 수 없기 때문에 방문될 수 없다
 *
 * Point
 * - 녹게 되는 치즈칸을 모두 구한후
 *   한번에 녹게 해주어야 한다
 *   + 방문하는 순서대로 족족 녹게하면...
 *     모든 치즈가 녹아버릴걸?
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int N, M;
int board[100][100];
bool isVisited[100][100];
enum Status { AIR, CHEESE, HOLE };
struct Point { int y, x; };
int dy[4] = {+1, 0, 0, -1};
int dx[4] = {0, +1, -1, 0};

// Set up : Functions Declaration
bool isValid(int y, int x);
void checkStatus();
void cheeseMelt();
int residualCheese();


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    cin >> N >> M;
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            cin >> board[i][j];
        }
    }

    // Process
    int time = 0; /* 치즈가 모두 녹아서 없어지는 데 걸리는 시간 */
    int res, prevRes = -1; /* res = 현재 남은 치즈칸 개수
                            * prevRes = 한 시간 전에 남아 있는 치즈칸 개수 */
    while ((res = residualCheese()) > 0) { /* 현재 남아있는 치즈칸이 있으면 */
        time++; /* 시간 증가 */
        checkStatus(); /* 공기칸, 치즈칸, 구멍칸 확인 */
        cheeseMelt(); /* 공기에 닿은 치즈 녹음 */
        prevRes = res; /* res 는 녹기 전에 측정한 남아있는 치즈칸이므로
                        * prevRes 에 그 값을 저장 */
    }

    // Control : xOutput
    cout << time << endl;
    cout << prevRes << endl;
}

// Helper Functions
bool isValid(int y, int x)
/* 좌표 (y,x) 가 유효하면 true 를 반환, 그 외 false 를 반환 */
{
    return y >= 0 && y < N && x >= 0 && x < M;
}

void checkStatus()
/* 공기칸, 치즈칸, 구멍칸 확인하는 함수 */
{
    /* 판의 가장자리 (0,0) 을 시작으로 BFS */
    queue<Point> que;
    memset(isVisited, false, sizeof(isVisited));
    que.push({0, 0});
    isVisited[0][0] = true;

    while (not(que.empty())) {
        int cy = que.front().y;
        int cx = que.front().x;
        que.pop();

        board[cy][cx] = AIR; /* BFS 로 방문된 모든 칸은 공기칸임 */
        for (int i=0; i<4; i++) {
            int ny = cy + dy[i];
            int nx = cx + dx[i];
            if (isValid(ny, nx) && board[ny][nx] != CHEESE
                && not(isVisited[ny][nx]))
            {
                isVisited[ny][nx] = true;
                que.push({ny, nx});
            }
        }
    }

    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            /* BFS 로 방문되지 않고 치즈가 아닌 칸은 구멍칸임 */
            if (not(isVisited[i][j]) && board[i][j] != CHEESE) {
                board[i][j] = HOLE;
            }
        }
    }
}

void cheeseMelt()
/* 공기에 닿은 치즈가 녹는 것을 구현한 함수 */
{
    queue<Point> que; /* 녹게 될 치즈칸의 좌표가 담긴 Queue */
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            if (board[i][j] == CHEESE) {
                for (int k=0; k<4; k++) {
                    int ay = i + dy[k];
                    int ax = j + dx[k];
                    /* 인접한 칸 (ay,ax) 가 공기이면 */
                    if (isValid(ay, ax) && board[ay][ax] == AIR) {
                        que.push({i, j}); /* que 에 넣어줌 */
                        break;
                    }
                }
            }
        }
    }

    /* que 에 담긴 모든 치즈칸은 녹아서 공기칸이 됨 */
    while (not(que.empty())) {
        int cy = que.front().y;
        int cx = que.front().x;
        que.pop();
        board[cy][cx] = AIR;
    }
}

int residualCheese()
/* 보드에 남아있는 치즈칸의 개수를 반환 */
{
    int res = 0;
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            if (board[i][j] == CHEESE) { res++; }
        }
    }
    return res;
}
profile
이런 미친 게임을 봤나! - 옥냥이

0개의 댓글