[백준 14502. 연구소]

youngtae·2023년 3월 28일
0
post-thumbnail

문제

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

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.

2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.

2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.

2 1 0 0 1 1 0
1 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 1 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

바이러스가 퍼진 뒤의 모습은 아래와 같아진다.

2 1 0 0 1 1 2
1 0 1 0 1 2 2
0 1 1 0 1 2 2
0 1 0 0 0 1 2
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.


풀이1

처음 풀이에서는 모든 통로에 벽을 세우면서 브루트 포스로 BFS하면서 최댓값을 갱신했다
1. 벽이 3개 세워지면 BFS 시작 후 바이러스 위치를 큐에 삽입
2. 테스트를 위해 기존 배열을 deepcopy해서 탐색
3. 큐가 비면 안전영역 카운트 후 갱신
4. 백트래킹하며 반복

# memory: 217996KB  time: 2740ms (pypy)
import sys
from collections import deque
import copy
from itertools import combinations

input = sys.stdin.readline
# 벽의 위치 모든 경우의 수를 따져봐야함
# 브루트포스 하면서 BFS 탐색
N, M = map(int, input().split())
arr = [list(map(int, input().split())) for _ in range(N)]
q = deque()
safe = 0
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


def BFS():
    # 바이러스 위치 큐에 삽입
    # 벽 위치 바뀔때마다 BFS하므로 함수 안에서 바이러스 위치 큐에 삽입
    for i in range(N):
        for j in range(M):
            if arr[i][j] == 2:
                q.append([i, j])

    # 테스트용 배열
    test = copy.deepcopy(arr)
    # 바이러스 확산
    while q:
        cx, cy = q.popleft()
        for k in range(4):
            nx = cx + dx[k]
            ny = cy + dy[k]
            if 0 <= nx < N and 0 <= ny < M:
                if test[nx][ny] == 0:
                    test[nx][ny] = 2
                    q.append([nx, ny])
    # 안전 영역
    global safe
    cnt = 0
    # 안전영역 카운트 후 갱신
    for j in range(N):
        for k in range(M):
            if test[j][k] == 0:
                cnt += 1
    safe = max(safe, cnt)


벽이 3개 세워지면 BFS 시작
def wall(num):
    if num == 3:
        BFS()
        return

    for j in range(N):
        for k in range(M):
            # 백트래킹
            if arr[j][k] == 0:
                arr[j][k] = 1
                wall(num+1)
                arr[j][k] = 0




BFS()
print(safe)

하지만 이렇게 풀 경우 pypy에서는 풀리지만 python에서는 시간 초과가 났다.
더 좋은 풀이가 있을 것 같아 찾아 본 결과 조합을 사용해서 푼 풀이를 참고하였다.


풀이2

  1. BFS함수 내부에서 통로 위치를 모두 리스트에 추가한다.
  2. combinations 라이브러리를 사용해서 벽을 3개 세우는 경우의 수를 모두 불러온다.
  3. 각 경우의 수 마다 해당 위치에 벽을 세우고 바이러스 위치를 큐에 삽입
  4. 테스트를 위해 기존 배열을 deepcopy해서 탐색
  5. while 문으로 탐색 (기존 풀이와 동일)

기존 풀이에서 백트래킹 하며 벽을 3개 세우던 부분을 조합을 사용함으로써 시간을 많이 줄일 수 있는 풀이였다.

# memory: 34200KB  time: 2796ms (python)

import sys
from collections import deque
import copy
from itertools import combinations

input = sys.stdin.readline
# 벽의 위치 모든 경우의 수를 따져봐야함
# 브루트포스 하면서 BFS 탐색
N, M = map(int, input().split())
arr = [list(map(int, input().split())) for _ in range(N)]
q = deque()
safe = 0
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

# 조합을 이용한 풀이
road = []
def BFS():
    # 통로 위치 리스트에 추가
    for i in range(N):
        for j in range(M):
            if arr[i][j] == 0:
                road.append([i, j])
    # 통로 중 3개 골라 조합 만들기
    for wall in combinations(road, 3):
        # 테스트용 배열
        test = copy.deepcopy(arr)
        # 조합의 좌표에 벽 생성
        for x, y in wall:
            test[x][y] = 1
        # 바이러스 위치 큐에 삽입
        for i in range(N):
            for j in range(M):
                if arr[i][j] == 2:
                    q.append([i, j])

        # 바이러스 확산
        while q:
            cx, cy = q.popleft()
            for k in range(4):
                nx = cx + dx[k]
                ny = cy + dy[k]
                if 0 <= nx < N and 0 <= ny < M:
                    if test[nx][ny] == 0:
                        test[nx][ny] = 2
                        q.append([nx, ny])
        # 안전 영역
        global safe
        cnt = 0
        # 안전영역 카운트 후 갱신
        for j in range(N):
            for k in range(M):
                if test[j][k] == 0:
                    cnt += 1
        safe = max(safe, cnt)


BFS()
print(safe)
profile
나의 개발기록

0개의 댓글