티스토리 뷰
연구소 2 성공
문제
인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이러스는 퍼지게 된다.
연구소는 크기가 N×N인 정사각형으로 나타낼 수 있으며, 정사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.
일부 빈 칸은 바이러스를 놓을 수 있는 칸이다. 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다.
예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자. 0은 빈 칸, 1은 벽, 2는 바이러스를 놓을 수 있는 칸이다.
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 2 0 1 1
0 1 0 0 0 0 0
2 1 0 0 0 0 2
M = 3이고, 바이러스를 아래와 같이 놓은 경우 6초면 모든 칸에 바이러스를 퍼뜨릴 수 있다. 벽은 -, 바이러스를 놓은 위치는 0, 빈 칸은 바이러스가 퍼지는 시간으로 표시했다.
6 6 5 4 - - 2
5 6 - 3 - 0 1
4 - - 2 - 1 2
3 - 2 1 2 2 3
2 2 1 0 1 - -
1 - 2 1 2 3 4
0 - 3 2 3 4 5
시간이 최소가 되는 방법은 아래와 같고, 5초만에 모든 칸에 바이러스를 퍼뜨릴 수 있다.
0 1 2 3 - - 2
1 2 - 3 - 0 1
2 - - 2 - 1 2
3 - 2 1 2 2 3
3 2 1 0 1 - -
4 - 2 1 2 3 4
5 - 3 2 3 4 5
연구소의 상태가 주어졌을 때, 모든 빈 칸에 바이러스를 퍼뜨리는 최소 시간을 구해보자.
입력
첫째 줄에 연구소의 크기 N(5 ≤ N ≤ 50), 놓을 수 있는 바이러스의 개수 M(1 ≤ M ≤ 10)이 주어진다.
둘째 줄부터 N개의 줄에 연구소의 상태가 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스를 놓을 수 있는 칸이다. 2의 개수는 M보다 크거나 같고, 10보다 작거나 같은 자연수이다.
출력
연구소의 모든 빈 칸에 바이러스가 있게 되는 최소 시간을 출력한다. 바이러스를 어떻게 놓아도 모든 빈 칸에 바이러스를 퍼뜨릴 수 없는 경우에는 -1을 출력한다.
내 코드
import sys
import copy
from itertools import combinations
from collections import deque
#바이러스가 모두 퍼질 수 있는지 판단.
def isPossible():
for i in range(n):
for j in range(n):
if not visited[i][j] and arr_copy[i][j]==0: #방문하지 않은 점이 0이면 바이러스 못 퍼지는 것임.
return False
return True
#바뀐 배열에서 가장 큰 수 리턴.
def biggest(myArr):
tmp=0
for x in myArr:
tmp=max(tmp,max(x))
return tmp
def bfs(virus):
q=deque()
for i in range(n):
for j in range(n):
if arr_copy[i][j]==2: #바이러스 시작점을 모두 2로 세팅.
arr_copy[i][j]=0
elif arr_copy[i][j]==1: #기존의 벽(1)을 -1로 세팅.
arr_copy[i][j]=-1
#바이러스 시작 위치를 덱에 넣고, 방문처리 한다.
for a,b in virus:
q.append([a,b])
visited[a][b]=True
#BFS...
while q:
x,y=q.popleft()
for i in range(4):
nx=x+dx[i]
ny=y+dy[i]
if nx<0 or nx>=n or ny<0 or ny>=n:
continue
if not visited[nx][ny] and arr_copy[nx][ny]==0: #해당 좌표가 방문X + 0이면,
arr_copy[nx][ny]=arr_copy[x][y]+1 #값을 이전값+1로 세팅하고,
visited[nx][ny]=True #방문 처리.
q.append([nx,ny]) #덱에 집어넣는다.
#메인 코드 부분
n,m=map(int,sys.stdin.readline().split())
#북동남서
dx=[-1,0,1,0]
dy=[0,1,0,-1]
arr=[]
for _ in range(n):
arr.append(list(map(int,sys.stdin.readline().split())))
#바이러스 시작 위치들을 저장
save=[]
for i in range(n):
for j in range(n):
if arr[i][j]==2:
save.append([i,j])
#바이러스 위치들 중 조건만큼 뽑기
tmp=combinations(save,m)
answer=1e9
flag=False
for virus in tmp:
arr_copy=copy.deepcopy(arr)
visited=[[False]*n for _ in range(n)]
bfs(virus)
if isPossible(): #바이러스가 모두 퍼질 수 있으면,
flag=True #가능이고,
answer=min(answer,biggest(arr_copy)) #답을 전체 값 중 최대값들의 최소로 설정
if flag: #바이러스가 모두 퍼질 수 있으면 답 출력.
print(answer)
else: #모두 퍼질 수 없으면 -1 출력.
print(-1)
풀이 및 접근)
- BFS 문제에 약간의 구현력이 들어간 문제이다.
- 어떤 지점들을 선택해 바이러스를 둬야할지는 모두 비교해 보아야 알 수 있다. 따라서 바이러스를 둘 수 있는 위치들을 저장해서 모두 비교해 본다.
- 바이러스의 진행은 동시에 이루어지므로, 우선 덱에 바이러스의 시작 위치들을 넣은 후, 그것들에 대해 BFS를 실시하면 바이러스가 동시에 진행되는 상황을 만들 수 있다.
- BFS를 하기 전, 바이러스 위치들을 0으로, 벽을 -1로 세팅하여 진행한다. 그래야 바이러스가 진행되면서 값들을 +1 하여 최종 답을 얻어 낼 수 있다.
- 모든 곳에 바이러스가 진행되지 않을 수도 있으므로, 이를 검증하는 함수 또한 작성하였다.
'백준 > BFS, DFS' 카테고리의 다른 글
백준 17135: 캐슬 디펜스 - BFS, 브루트포스 (파이썬, 자바) (0) | 2024.02.21 |
---|---|
백준 1261: 알고스팟 - 0 1 BFS, 다익스트라 (파이썬) (0) | 2024.01.16 |
백준 2206: 벽 부수고 이동하기 - BFS(파이썬) (0) | 2023.11.23 |
백준 7562: 나이트의 이동 - BFS(파이썬) (2) | 2023.10.30 |
백준 11725: 트리의 부모 찾기 - DFS(파이썬) (0) | 2023.10.12 |