컴퓨터/PS

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

도도새 도 2023. 10. 22. 20:21

백준 2636 치즈

 

백준 2636번을 풀었다. 한 번 녹일때 치즈의 겉만 녹인다, 라는 재미있는 조건이 있었는데, 이를 어떻게 처리해야 할 지에 대해 고민하게 해 준 문제였다.

 

시도 1 - 오답

 

#include <iostream>
#include <vector>
#include <queue>
#define MAX_VAL 100

using namespace std;

struct point {
    int x;//j
    int y;//i
};

queue<point> tempQueue;
queue<point> processingQueue;
queue<point> saveQueue;

int dX[] = { -1, 0, 1, 0 };
int dY[] = { 0, -1, 0, 1 };

int main(void) {
    int tY, tX;
    cin >> tY >> tX;
    vector<vector<int>> table(tY, vector<int>(tX, 0));
    vector<vector<bool>> checked(tY, vector<bool>(tX, 0));

    int day = 0;
    for (int i = 0; i < tY; i++) {
        for (int j = 0; j < tX; j++) {
            cin >> table[i][j];
        }
    }

    processingQueue.push({ 0, 0 });
    checked[0][0] = true;


    int checkCnt = 1;
    while (!processingQueue.empty()) {
        day++;
        while (!processingQueue.empty()) {

            point now = processingQueue.front();
            checked[now.y][now.x] = true;
            table[now.y][now.x] = 0;
            processingQueue.pop();

            for (int i = 0; i < 4; i++) {
                int nextX = now.x + dX[i];
                int nextY = now.y + dY[i];

                bool canGoCondition = nextX >= 0 && nextX < tX&& nextY >= 0 && nextY < tY && !checked[nextY][nextX];
                if (canGoCondition) {
                    if (table[nextY][nextX] == 1) {
                        tempQueue.push({ nextX, nextY });
                        if (checked[nextY][nextX] == false) {
                            checked[nextY][nextX] = true;
                            checkCnt++;
                        }
                    }
                    else {
                        processingQueue.push({ nextX, nextY });
                        if (checked[nextY][nextX] == false) {
                            checked[nextY][nextX] = true;
                            checkCnt++;
                        }
                    }
                }
            }
        }
        while (!tempQueue.empty()) {
            point nextPoint = tempQueue.front();
            processingQueue.push(nextPoint);
            saveQueue.push(nextPoint);
            tempQueue.pop();

            table[nextPoint.y][nextPoint.x] = 0;
        }

        if (checkCnt >= tX * tY) break;
        while (!saveQueue.empty()) {
            saveQueue.pop();
        }

    }

    int result = 0;
    while (!saveQueue.empty()) {
        saveQueue.pop();
        result++;
    }

    cout << day << "\n" << result;

    return 0;
}

위 코드로 95% 테스트 케이스를 통과하지 못하였다.

내가 틀린 반례는 아래와 같다.

5 5
0 0 0 0 0
0 1 1 0 0
0 1 0 1 0
0 1 1 1 0
0 0 0 0 0
answer 1,7

 

즉, 내 코드는 모든 테이블을 다 확인하는 것이 탈출 조건이었고 한 번 돌면서 확인할 떄마다(bfs 할 때마다) 하루가 걸렸다.

나의 코드의 경우 위 테스트 케이스 결과가 2, 0이 나왔다. 다 돌아보는데 이틀이 걸리고 그 전날 있던 치즈는 0이라는 결과가 나온 것이다.

 

 

시도 2 - 정답

 

이를 해결하기 위해서 탈출 조건을 수정하였다.

원래 : 모든 테이블을 다 체크했을 경우 탈출

변경 : 모든 치즈를 다 검토했을 경우 탈출

바뀐 코드는 아래와 같다.

 

#include <iostream>
#include <vector>
#include <queue>
#define MAX_VAL 100

using namespace std;

struct point {
    int x;//j
    int y;//i
};

queue<point> tempQueue;
queue<point> processingQueue;
queue<point> saveQueue;

int dX[] = { -1, 0, 1, 0 };
int dY[] = { 0, -1, 0, 1 };

int main(void) {
    int tY, tX;
    cin >> tY >> tX;
    vector<vector<int>> table(tY, vector<int>(tX, 0));
    vector<vector<bool>> checked(tY, vector<bool>(tX, 0));
    int cheeseCnt = 0;
    int cheeseCheckCnt = 0;
    int day = 0;
    for (int i = 0; i < tY; i++) {
        for (int j = 0; j < tX; j++) {
            int num;
            cin >> num;
            table[i][j] = num;
            if(num == 1) cheeseCnt++;
        }
    }

    processingQueue.push({ 0, 0 });
    checked[0][0] = true;

    while (!processingQueue.empty()) {
        day++;
        while (!processingQueue.empty()) {//치즈 겉을 녹이는 동작

            point now = processingQueue.front();
            checked[now.y][now.x] = true;
            table[now.y][now.x] = 0;
            processingQueue.pop();

            for (int i = 0; i < 4; i++) {
                int nextX = now.x + dX[i];
                int nextY = now.y + dY[i];

                bool canGoCondition = nextX >= 0 && nextX < tX&& nextY >= 0 && nextY < tY && !checked[nextY][nextX];
                if (canGoCondition) {
                    if (table[nextY][nextX] == 1) {//확인한 곳이 치즈라면
                        tempQueue.push({ nextX, nextY });//다음에 BFS를 시작한 큐에 넣고
                        checked[nextY][nextX] = true;//방문을 체크하고
                        cheeseCheckCnt++;//확인한 치즈 개수를 올린다.

                    }
                    else {//확인한 곳이 치즈가 아니라면
                        processingQueue.push({ nextX, nextY });//당장 방문할 큐에 넣고
                        checked[nextY][nextX] = true;//방문을 체크한다.
                    }
                }
            }
        }
        while (!tempQueue.empty()) {//다음에 BFS를 시작할 큐에서 바로 사용할 큐로 요소를 옮긴다.
            point nextPoint = tempQueue.front();
            processingQueue.push(nextPoint);
            saveQueue.push(nextPoint);//정답으로 사용될 큐
            tempQueue.pop();
        }

        if (cheeseCheckCnt == cheeseCnt) break;//종료 조건 : 치즈 개수가 다 채워짐
        while (!saveQueue.empty()) {//정답으로 사용될 큐를 비움
            saveQueue.pop();
        }
    }

    int result = 0;
    while (!saveQueue.empty()) {//정답의 개수를 확인
        saveQueue.pop();
        result++;
    }

    cout << day << "\n" << result;

    return 0;
}

드디어 정답이다.