백준 14502번 - 연구소

연구소 문제 바로 가기

풀이 날짜: 2026-01-07

baekjoon 알고리즘 문제 풀이

풀이 코드


import sys
input = sys.stdin.readline

dx = [0,0,-1,1]
dy = [-1,1,0,0]

n,m = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(n)]
temp = [[0]*m for _ in range(n)]

result = 0

empty = []
for i in range(n):
    for j in range(m):
        if graph[i][j] == 0:
            empty.append((i,j))

def virus(x,y):
    for i in range(4):
        nx = dx[i] + x
        ny = dy[i] + y
        if 0<=nx<n and 0<=ny<m and temp[nx][ny] == 0:
            temp[nx][ny] = 2
            virus(nx,ny)

def get_score():
    score = 0
    for i in range(n):
        for j in range(m):
            if temp[i][j] == 0:
                score += 1
    return score

def dfs(start, count):
    global result
    if count == 3:
        for i in range(n):
            for j in range(m):
                temp[i][j] = graph[i][j]

        for i in range(n):
            for j in range(m):
                if temp[i][j] == 2:
                    virus(i,j)

        result = max(result, get_score())
        return

    for i in range(start, len(empty)):
        x,y = empty[i]
        if graph[x][y] == 0:
            graph[x][y] = 1
            dfs(i+1, count+1)
            graph[x][y] = 0
dfs(0,0)
print(result)
  

문제 핵심 요약:

  • 빈칸 3개를 골라 벽을 세운 뒤, 바이러스를 퍼뜨렸을 때
  • 남아있는 안전영역(0의 개수)이 최대가 되는 값을 구하는 문제이다.

즉, ‘최적의 3칸 선택’이 핵심이다.
결국 빈칸 중 3개를 조합으로 고르고(완전탐색) 바이러스 확산 시뮬레이션을 돌린 후 안전영역 계산을 반복하면 된다.


내가 선택한 풀이 방식

1) 벽 3개 세울 위치를 ‘조합’으로 뽑기 빈칸(0) 리스트를 empty에 미리 모아뒀다.
이 빈칸들 중 서로 다른 3개를 골라야 하니까

  • DFS에서 start 인덱스를 두고
  • for i in range(start, len(empty)) 방식으로 탐색하면
  • 같은 조합을 중복해서 뽑지 않게 된다.
  • dfs(i+1, count+1)로 다음 선택은 항상 더 뒤에서만 하게 만들어서 중복을 방지한다.

2) 벽을 세운 상태를 temp에 복사하기 벽 3개를 다 세웠다면(count == 3)

  • temp에 현재 graph 상태를 그대로 복사한다.
  • 이후 바이러스 확산은 temp에서만 수행한다. 왜냐하면,
  • 원본 graph는 다음 조합 탐색을 위해 항상 깨끗해야 한다.
  • 따라서 매 조합마다 temp를 새로 만든 뒤, 그 복사본에서만 퍼뜨리는 방식이 안전하다.

3) 바이러스 확산 temp[i][j]==2인 모든 위치에서 바이러스 확산을 시작한다.

  • 상하좌우로 이동하면서 0이면 2로 바꾸고
  • 그 위치에서 다시 퍼뜨린다(재귀)
  • 즉, 연결된 0 영역을 전부 감염시킨다.

4) 최댓값 갱신 바이러스 확산이 끝난 temp에서 0의 개수를 세면 ‘안전영역’이 된다.

  • result = max(result, get_score())

백트래킹의 본질

왜 graph[x][y] = 0 으로 반드시 되돌려야 할까?

이 문제에서 가장 헷갈렸던 부분은, 왜 벽을 세운 뒤에 다시 0으로 되돌려야 하는가였다.

DFS는 ‘여러 개의 지도’를 쓰는 게 아니라 ‘하나의 지도’를 돌려 쓴다.

이 문제에서 graph는 매 경우마다 새로 만들어지는 것이 아니라, 하나의 graph를 계속 수정하면서 여러 경우를 시도하는 것이다.
즉, DFS는

  • 같은 지도 위에 벽을 세웠다가
  • 그 실험이 끝나면 다시 원래 상태로 돌아와서
  • 다음 선택을 시도하는 구조였다.

따라서 DFS의 내용은 다음과 같다.

  • graph[x][y] = 1 : “이 선택지를 한 번 선택해본다”
  • dfs(…) : “이 선택을 했을 때 벌어질 수 있는 모든 경우를 끝까지 본다”
  • graph[x][y] = 0 : “이 선택은 취소하고, 다음 선택을 공정하게 보기 위해 원상복구한다”