문제
인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.
연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.
일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.
예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.
이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.
2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.
바이러스가 퍼진 뒤의 모습은 아래와 같아진다.
벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.
연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)
둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.
빈 칸의 개수는 3개 이상이다.
출력
첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.
초기접근
벽을 어디다가 세울지 어떻게 계산하지?
효율적인 방법을 찾을 수 없을것 같아서 벽을 세우는 모든 경우의 수를 세보았는데
가로세로 길이가 최대 8 이므로 최대 64칸.
단순하게 64칸에서 3칸을 뽑을 확률을 계산했을때
64 C 3 = 41664.
이정도면 벽 세우는건 모든 경우의 수 다 따져보면 되겠다.
아이디어 정립
1. 벽 3개를 세우는 모든 경우의 수 마다,
2. 바이러스가 모두 퍼져나갔을 때의
3. 안전지대 개수를 구하고
4. 안전지대 개수가 가장 클 때의 값을 구한다.
바이러스가 퍼져나가는 것은 DFS로 구현한다.
정답 코드
from itertools import combinations
def dfs(graph, start):
x = start[0]
y = start[1]
graph[x][y] = 2
# 상 하 좌 우
dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]
for d in dir:
nowx = x+d[0]
nowy = y+d[1]
if nowx < 0 or nowx >= n or nowy < 0 or nowy >= m:
continue
if graph[nowx][nowy] == 0:
dfs(graph, [nowx, nowy])
def initLab(sourceLab):
reLab = [[0]*len(sourceLab[0]) for i in range(len(sourceLab))]
for i in range(len(sourceLab)):
for j in range(len(sourceLab[0])):
reLab[i][j] = sourceLab[i][j]
return reLab
n, m = map(int, input().split())
lab = []
zero = []
start = []
for i in range(n):
lab.append(list(map(int, input().split())))
for j in range(m):
if lab[i][j] == 0:
zero.append([i, j])
if lab[i][j] == 2:
start.append([i, j])
pickwall = list(combinations(zero, 3))
result = []
# 벽 세개 세우는 경우 다 해보기
for i in range(len(pickwall)):
tempLab = initLab(lab)
for p in pickwall[i]:
x = p[0]
y = p[1]
tempLab[x][y] = 1
# 모든 바이러스로부터 시작
for st in start:
dfs(tempLab, st)
# 안전지대 개수 세기
count = 0
for j in range(n):
for k in range(m):
if tempLab[j][k] == 0:
count += 1
result.append(count)
print(max(result))
헤맸던 점
처음에는 tempLab = initLab(lab) 처럼 새로 리스트를 초기화 시켜주는 것이 아닌
tempLab = lab * [len(pickwall)] 로 미리 리스트를 만들어 놓고 차례로 쓰려고 했었다.
그런데 tempLab = lab * [len(pickwall)] 이렇게 코드를 작성하고 실행했을때
tempLab[0]을 수정해도 tempLab[1], tempLab[2] .. 같이 나머지의 리스트원소도 값이 바뀌었다.
리스트가 모든 원소에 다 "참조"로 들어간 것이다.
파이썬의 유일한 단점이 list() = list()를 해줬을때 항상 참조가 된다는 점이다.
다른 문제에서는 여기서 헤매지 말고 신속히 리스트에 값을 직접 넣는 함수를 따로 만들자.
'Algorithm > DFS BFS' 카테고리의 다른 글
[삼성 역량테스트] 인구이동 (0) | 2021.01.27 |
---|---|
[삼성 코테] 연산자 끼워넣기 (0) | 2021.01.26 |
[백준] 감시 피하기 (0) | 2021.01.26 |
[카카오코테] 괄호변환 (0) | 2021.01.25 |
[백준] 경쟁적 전염 (2) | 2021.01.22 |