본문 바로가기

Programming/BOJ

BOJ 17142 · 연구소 3


알고리즘 분류 : BFS, 브루트 포스  


연구소에 바이러스를 퍼트리는 데 걸리는 최소 시간을 구하는 문제다. 바이러스를 놓을 수 있는 경우를 모두 만든 후, 각 케이스에 대해 BFS로 바이러스를 퍼트려서 최솟값을 구하면 된다. BOJ 17141번 '연구소 2'와 거의 동일한 문제이다. 일부 값만 수정하면 된다.


  • 연구소의 사이즈는 N이며, 바이러스의 개수는 M이다.
  • 바이러스를 놓을 수 있는 칸(2)의 좌표를 별도의 리스트 V에 저장한다.
  • 빈칸(0)의 개수를 모두 세고, 이를 K라 하자. 이 값은 바이러스를 퍼트려야 하는 칸의 총개수가 된다.
  • 조합(combination) 방법을 통해 M개의 바이러스를 바이러스 칸(2)에 둔다. 이때 리스트 V를 활용하여, 바이러스를 놓은 칸의 좌표를 큐에 모두 집어넣는다.
  • BFS를 통해 바이러스를 감염시킨다. 감염시킬 때마다, 감염시킨 칸의 개수를 세고, 감염 시간을 계속 업데이트한다.
  • 감염시킬 수 있는 곳은 빈칸(0)만 가능하지만, 바이러스를 놓을 수 있는 칸(2)으로도 이동할 수 있다.
  • BFS가 종료된 후, 감염시킨 칸의 개수와 K가 같은지 비교한다. 만약 비교한 값이 같고, 걸린 시간이 최솟값이라면, 정답으로 업데이트한다.
  • 모든 경우의 수에 대해서 BFS를 수행한다.





C++ 소스코드


#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;

struct virus {
    int x, y;
};

int a[50][50], dist[50][50];
int n, m, k, ans=1e9;
bool select[10];
vector<virus> v;
queue<virus> q;
const int dx[] = {-1, 0 , 1, 0}, dy[] = {0, 1, 0, -1};

void bfs() {
    int infect=0, times=0;
    while (!q.empty()) {
        int x = q.front().x, y = q.front().y; q.pop(); 
        for (int i=0; i<4; i++) {
            int nx = x+dx[i], ny = y+dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
            if (a[nx][ny] != 1 && dist[nx][ny] == -1) {
                dist[nx][ny] = dist[x][y]+1;
                if (a[nx][ny] == 0) {
                    infect += 1;
                    times = dist[nx][ny];
                }
                q.push({nx, ny});
            }
        }
    }
    if (infect == k && ans > times) ans = times;
}

void solve(int idx, int cnt, int d) {
    if (cnt == m) {
        memset(dist, -1, sizeof(dist));
        for (int i=0; i<d; i++) {
            if (select[i]) {
                q.push({v[i].x, v[i].y});
                dist[v[i].x][v[i].y] = 0;
            }
        }
        bfs();
        return;
    }
    for (int i=idx; i<d; i++) {
        if (!select[i]) {
            select[i] = true;
            solve(i+1, cnt+1, d);
            select[i] = false;
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i=0; i<n; i++) {
        for (int j=0; j<n; j++) {
            scanf("%d", &a[i][j]);
            if (a[i][j] == 2) v.push_back({i, j});
            if (a[i][j] == 0) k += 1;
        }
    }
    solve(0, 0, v.size());
    printf("%d\n", ans == 1e9 ? -1 : ans);
    return 0;
}




Python 3 소스코드


from collections import deque

n, m = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)]
select = [False] * 10
q, v, k = deque(), [], 0

def bfs(dist):
    global ans
    infect, times = 0, 0
    while q:
        x, y = q.popleft()
        for dx, dy in (-1,0), (1,0), (0,1), (0,-1):
            nx, ny = x+dx, y+dy
            if nx < 0 or nx >= n or ny < 0 or ny >= n:
                continue
            if a[nx][ny] != 1 and dist[nx][ny] == -1:
                dist[nx][ny] = dist[x][y]+1
                q.append((nx, ny))
                if a[nx][ny] == 0:
                    infect += 1
                    times = dist[nx][ny]
    if infect == k:
        ans = min(ans, times)

def solve(idx, cnt, d):
    if cnt == m:
        dist = [[-1]*n for _ in range(n)]
        for i in range(d):
            if select[i]:
                x, y = v[i]
                q.append((x, y))
                dist[x][y] = 0
        bfs(dist)
        return
    for i in range(idx, d):
        if not select[i]:
            select[i] = True
            solve(i+1, cnt+1, d)
            select[i] = False

for i in range(n):
    for j in range(n):
        if a[i][j] == 2:
            v.append((i, j))
        if a[i][j] == 0:
            k += 1
ans = 10**9
solve(0, 0, len(v))
print(ans if ans != 10**9 else -1)




참고



태그

  • 질문입니다 2019.04.23 00:38 신고 댓글주소 수정/삭제 댓글쓰기

    이렇게 되면,
    2 2 2 1 1
    2 1 1 1 1
    2 1 1 1 1
    2 1 1 1 1
    2 2 2 1 1 일경우 답이 0이 나오게 되나요?

    이미 2는 바이러스가 있기 때문에 저부분은 시간을 더해주면 안되는데, 이 것을 고려해주는 코드가 없는 것 같아서요!

    • 안녕하세요
      예제 7번 입력에 대한 질문이 맞으신가요?
      소스코드 컴파일해보시면 0이 나오는 것을 확인하실 수 있습니다.

      C++ 코드 기준 27번 라인을 보시면,
      if (a[nx][ny] == 0) 구문으로 이동한 칸이 빈칸인지 확인합니다. 빈칸인 경우에만 바이러스 카운트(infect += 1)를 하므로, 말씀하신 부분을 고려하고 있습니다.

    • 질문입니다 2019.04.23 12:17 신고 댓글주소 수정/삭제

      감사드립니다! ㅠㅠ