알고리즘/문제 풀이

14502번: 연구소

Themion 2022. 1. 11. 13:59

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

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

연구소의 빈 칸과 벽, 바이러스가 있는 칸을 노드로, 상하좌우로 인접한 노드 사이의 관계를 에지로 보면 이 문제는 그래프 탐색 문제로 볼 수 있다. 이 때 벽을 세 칸 세워야 하므로 브루트포스를 이용해 각 빈 칸에 벽을 세 개 세운 뒤 그래프 탐색을 실행해야 한다.

#include <iostream>
#include <queue>

using namespace std;

typedef pair<int, int> coord;

#define MAX_N 8
#define _y first
#define _x second

// N, M: 연구실의 실제 크기, map: 연구실의 배치도
int N, M, map[MAX_N][MAX_N];
// 바이러스의 위치의 집합
queue<coord> virus;

int max(int a, int b) { return a > b ? a : b; }

// 좌표값을 입력받아 그 좌표의 다음 좌표를 반환
coord next(coord c) {
    c._x = (c._x + 1) % M;
    c._y += c._x == 0;

    return c;
}

// 좌표를 인자로 받아 그 좌표가 연구실 안의 좌표인지를 반환
bool valid(coord c)
{ return c._y >= 0 && c._x >= 0 && c._y < N && c._x < M; }

// 벽을 세운 뒤 바이러스로부터 안전한 공간을 계산
int fill_virus() {
    // m: map을 보존하기 위해 별도의 공간에 복사하여 계산
    // ret: 바이러스로부터 안전한 공간의 수
    int m[MAX_N][MAX_N], ret = 0;
    // BFS를 하는 과정에서 바이러스의 원래 위치를 잃어버릴 수 있으므로
    // 별도의 공간에 복사하여 계산
    queue<coord> q = virus;

    // map을 m에 복사하면서 빈 공간의 수를 계산
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) {
        m[i][j] = map[i][j];
        if (!map[i][j]) ret++;
    }

    // BFS를 통해 연구실을 바이러스로 채운다
    while (!q.empty()) {
        coord c = q.front();
        q.pop();

        // 바이러스의 상하좌우가 연구실 내부이면서 빈 공간일 때
        for (coord crd: { coord(c._y + 1, c._x), coord(c._y, c._x + 1),
                          coord(c._y - 1, c._x), coord(c._y, c._x - 1) })
            if (valid(crd) && !m[crd._y][crd._x]) {
                // 해당 좌표를 q에 넣은 뒤 공간을 바이러스로 채운다
                q.push(crd);
                m[crd._y][crd._x] = 2;
                ret--;
            }
    }

    // 남은 빈 공간의 수를 반환
    return ret;
}

// 벽을 cnt개 세울 수 있을 때 좌표 c부터 차례로 벽을 세운다
int make_wall(coord c, int cnt) {
    // 벽을 모두 세웠다면 공간을 바이러스로 채워 안전한 칸의 수를 계산
    if (!cnt) return fill_virus();
    
    // 안전한 칸의 수를 계산해 저장할 변수
    int ret = 0;

    // 벽을 세울 좌표가 연구소 안의 좌표일 때
    while (valid(c)) {
        // 해당 좌표가 빈 공간이라면 벽을 세운 뒤 다음 단계를 계산하고 벽을 철거
        if (!map[c._y][c._x]) {
            map[c._y][c._x] = 1;
            ret = max(ret, make_wall(next(c), cnt - 1));
            map[c._y][c._x] = 0;
        }

        // 다음 좌표에서 벽을 세운다
        c = next(c);
    }

    // 안전한 칸 수의 최댓값을 반환
    return ret;
}

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

    // 문제의 조건을 입력받으면서 바이러스의 좌표는 따로 저장
    cin >> N >> M;
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) {
        cin >> map[i][j];
        if (map[i][j] == 2) virus.push({ i, j });
    }

    // 안전 영역의 최대 크기를 출력
    cout << make_wall({0, 0}, 3) << '\n';

    return 0;
}

'알고리즘 > 문제 풀이' 카테고리의 다른 글

14681번: 사분면 고르기  (0) 2022.01.11
14656번: 조교는 새디스트야!!  (0) 2022.01.11
14500번: 테트로미노  (0) 2022.01.11
14490번: 백대열  (0) 2022.01.11
14425번: 문자열 집합  (0) 2022.01.11