문제 풀이/백준

백준 - 연구소 3 (17142) [C++]

JJJaewon 2024. 9. 14. 16:16
반응형

이 문제는 백트래킹과 bfs를 이용해서 풀었다.

 

시간 제한이 0.25초이므로, C++ 기준 1초에 약 1억번의 연산을 가정했을 때 2,500만의 연산으로 문제를 풀어야 한다.

 

연구소의 크기는 가장 크게 잡았을 때 50 * 50이고, 최대 바이러스의 수 10개 중 가장 많은 경우의 수를 뽑는 조합인 10C5 = 252이므로 252 * 50 * 50 = 약 630,000 정도의 연산이 소요된다.

따라서 백트래킹을 통해 활성화할 바이러스를 선택하고, 해당 바이러스로 bfs를 돌려 문제를 풀 수 있다.

 

여기서 한 가지 주의할 점은 bfs가 끝나고 걸린 시간을 셀 때 연구소 기준 2인 곳은 생략해야 한다는 점이다. 그렇지 않으면 첫번째 예제와 두번째 예제에서 답이 5가 나오게 된다. 모든 바이러스가 활성화된 바이러스여야한다는 기준이 없으므로 빈칸만이 바이러스로 바뀐 경우만 세어주면 정답이 나온다.

#include <iostream>
#include <utility>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

#define MAX 50
#define EMPTY 0
#define WALL 1
#define VIRUS 2
#define INF 1000000000

int N, M;

vector<pair<int, int>> allVirus;
int allSize;
int lab[MAX][MAX];
int answer = INF;
int dy[] = {-1, 0, 1, 0};
int dx[] = {0, -1, 0, 1};

bool inRange(int y, int x) {
    return (0 <= y && y < N && 0 <= x && x < N);
}

int getSeconds(vector<vector<int>>& dist) {
    int seconds = -1;
    
    for (int y = 0; y < N; y++) {
        for (int x = 0; x < N; x++) {
            if (lab[y][x] == VIRUS) {
                continue;
            }
            
            seconds = max(seconds, dist[y][x]);
        }
    }

    return seconds;
}

bool isAllVirus(vector<vector<int>>& dist) {
    for (int y = 0; y < N; y++) {
        for (int x = 0; x < N; x++) {
            if (lab[y][x] == WALL) {
                continue;
            }

            if (lab[y][x] == EMPTY && dist[y][x] == -1) {
                return false;
            }
        }
    }

    return true;
}

int bfs(vector<pair<int, int>>& startVirus) {
    vector<vector<int>> dist(N, vector<int>(N, -1));
    queue<pair<int, int>> q;
    
    for (auto& start : startVirus) {
        dist[start.first][start.second] = 0;
        q.push(start);
    }

    while (!q.empty()) {
        int y = q.front().first;
        int x = q.front().second;
        q.pop();

        for (int i = 0; i < 4; i++) {
            int ny = y + dy[i];
            int nx = x + dx[i];

            if (inRange(ny, nx) &&
            dist[ny][nx] == -1 &&
            lab[ny][nx] != WALL) {
                dist[ny][nx] = dist[y][x] + 1;
                q.push({ny, nx});
            }
        }
    }

    int seconds = getSeconds(dist);
    
    if (isAllVirus(dist)) {
        return seconds;
    }

    return INF;
}

vector<pair<int, int>> getStart(vector<int>& index) {
    vector<pair<int, int>> result;

    for (int idx : index) {
        result.push_back(allVirus[idx]);
    }

    return result;
}

void solve(int prev, vector<int> index) {
    if (index.size() == M) {
        vector<pair<int, int>> tempStart = getStart(index);

        int tempAnswer = bfs(tempStart);

        answer = min(answer, tempAnswer);
        return;
    }

    for (int i = prev + 1; i < allSize; i++) {
        index.push_back(i);
        solve(i, index);
        index.pop_back();
    }
}

bool isAlreadyVirus() {
    for (int y = 0; y < N; y++) {
        for (int x = 0; x < N; x++) {
            if (lab[y][x] == EMPTY) {
                return false;
            }
        }
    }

    return true;
}

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

    cin >> N >> M;
    for (int y = 0; y < N; y++){
        for (int x = 0; x < N; x++) {
            cin >> lab[y][x];

            if (lab[y][x] == VIRUS) {
                allVirus.push_back({y, x});
            }
        }
    }

    if (isAlreadyVirus()) {
        cout << 0 << '\n';
        return 0;
    }

    allSize = allVirus.size();

    solve(-1, vector<int>());

    if (answer == INF) {
        cout << -1 << '\n';
        return 0;
    }
    cout << answer << '\n';

    return 0;
}
반응형