코딩/백준

[백준] 2636번 치즈 - C++

최선을 다하는 2023. 1. 20. 21:11

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

 

2636번: 치즈

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

www.acmicpc.net

문제

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

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

<그림 1> 원래 치즈 모양

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

<그림 2> 한 시간 후의 치즈 모양

<그림 3> 두 시간 후의 치즈 모양

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

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

입력

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

출력

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


BFS 순회를 하면서 치즈(1)를 만나면 공기(0) 로 바꾼다. 공기(0)를 만나면 queue에 추가한다.

해당 순회로 부터 녹인 치즈의 수가 총 치즈의 수와 같다면 반복문을 종료한다. 

 

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

typedef pair<int,int> point;
int N, M,cnt = 0,t = 0, tcnt = 0;
int mat[110][110];
int dx[4] = {1,0,-1,0};
int dy[4] = {0,1,0,-1};
bool vis[110][110];
queue <point> q;

void bfs(){
    q.push(make_pair(0,0));
    for(int i = 0 ; i < N ; i++)
        for(int j = 0 ; j < M ;j++)
            vis[i][j] = false;
    while(!q.empty()){
        int curx = q.front().first;
        int cury = q.front().second;
        q.pop();
        for(int k = 0 ; k < 4 ;k++){
            if(curx + dx[k] >= 0 && curx + dx[k] < N && cury + dy[k] >= 0 && cury + dy[k] < M){
                if (mat[curx + dx[k]][cury + dy[k]] == 0 && !vis[curx + dx[k]][cury + dy[k]]){
                    q.push(make_pair(curx + dx[k],cury + dy[k]));
                    vis[curx + dx[k]][cury + dy[k]] = true;
                }
                else if(mat[curx + dx[k]][cury + dy[k]] == 1 && !vis[curx + dx[k]][cury + dy[k]]){
                    vis[curx + dx[k]][cury + dy[k]] = true;
                    mat[curx + dx[k]][cury + dy[k]] = 0;
                    tcnt++;
                }
            }
        }
    }
}

int main (){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    cin >> N >> M;
    for(int i = 0 ; i < N ;i++){
        for(int j = 0 ; j < M ; j++){
            cin >> mat[i][j];
            if(mat[i][j])
                cnt++;
        }
    }
    while(++t){
        bfs();
        if(cnt == tcnt)
            break;
        cnt -= tcnt;
        tcnt = 0;
    }
    cout << t << "\n" << cnt;
}

 이 문제를 푼 것 같아서 확인해보니 문제 번호 2가 차이나는 2638번 치즈를 푼 적이 있었다. 자세히 보니 살짝 다른 문제였다!

 이런 문제를 풀때는 (1,1)로 매트릭스를 설정하고 테두리를 -1로 초기화 하는 방법으로 0~N-1 범위확인하는 조건문을 생략하곤 했다. 하지만 그렇게 풀었더니 시간초과가 났다. 도저히 시간을 줄일 방법이 없어 보여 검색을 해보니 다른 사람은 범위 확인 하는 조건문으로 문제를 풀어서 나도 그렇게 바꿔봤더니 문제가 풀리게 되었다. 조건문을 매번 확인 하는 것을 빼고 0 인지 1 인지 확인하는 방법이 더 간편하고 조건 확인도 덜 할 것 같아서 자주 애용했는데 이 방법이 시간이 더 걸린다는 것이 신기했다.