[Gold I]
https://www.acmicpc.net/problem/1194
1194번: 달이 차오른다, 가자.
첫째 줄에 미로의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 50) 둘째 줄부터 N개의 줄에 미로의 모양이 주어진다. 같은 타입의 열쇠가 여러 개 있을 수 있고, 문도 마찬가지이다. 그리고,
www.acmicpc.net
풀이
기본적으로 상하좌우 4방향으로 뻗어 나가는 흔한 BFS 형식으로 풀이하면 된다.
그런데 이제 비트마스킹을 곁들인.
열쇠와 문이 없다면, 각 row와 column, 그러니까 아래 코드에서는 visited[r][c]
2차원 배열만 채우며 진행하면 될 것이다.
그러나 열쇠가 있으므로.. 예를 들어, a열쇠를 가지고 (0,0)에 도달한 경우와, a열쇠가 없이 (0,0)에 도달한 경우는 전혀 다른 경우로 보고 진행해야 한다.
이를 비트마스킹으로 저장하였다.
예를 들면, A, C 문에 대한 열쇠 a, c가 있을 때의 flag 값은 101000인 식이다.
(line 16~18, line 41~51)
(1 ≤ N, M ≤ 50)
이기 때문에, 비트마스킹으로 인해 visited 배열이 3차원 배열이 되더라도,flag 6개만 check하므로 2^6 = 64가지 경우 정도.
따라서 공간 복잡도를 따져 봤을때,
50*50*64
정도.. 충분히 커버할 수 있는 범위이므로, 신경쓰지 않았다.
AC.
from collections import deque
N, M = map(int, input().split())
# 미로 입력
miro = []
for _ in range(N):
miro.append(list(input()))
# 시작 좌표 ("0") 탐색
start = ()
for r in range(N):
for c in range(M):
if miro[r][c] == "0":
start = (r, c)
# visited[r][c][flag]에 각 방문에서의 이동 횟수를 저장.
# flag 값은 A, B, C, D, E, F의 열쇠를 가지고 있는 지에 따라 BitMasking으로 저장.
# (111111 => A ~ F의 열쇠를 가짐 / 101000 => A, C의 열쇠를 가지고 있음.)
INF = 10e9
visited = [[[INF for _ in range((1 << 6))] for _ in range(M)] for _ in range(N)]
dr = [1, 0, -1, 0]
dc = [0, 1, 0, -1]
# BFS
deq = deque([(start[0], start[1], 0, 0)])
while deq:
r, c, cnt, flag = deq.popleft()
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
# 미로 벗어남 여부 체크 및 visited 체크
if 0 <= nr < N and 0 <= nc < M and visited[nr][nc][flag] == INF:
if miro[nr][nc] == "#": # 벽
continue
elif miro[nr][nc] == ".": # 빈칸
deq.append((nr, nc, cnt+1, flag))
visited[nr][nc][flag] = cnt+1
elif 97 <= ord(miro[nr][nc]) <= 102: # a ~ f 열쇠 칸
# 아스키코드, BitMasking을 통해 열쇠 보유 여부를 추가.
o = ord(miro[nr][nc]) - 97
newFlag = flag | (1 << o)
deq.append((nr, nc, cnt+1, newFlag))
visited[nr][nc][newFlag] = cnt+1
elif 65 <= ord(miro[nr][nc]) <= 70: # A ~ F 문 칸
o = ord(miro[nr][nc]) - 65
if flag & (1 << o): # 열쇠가 있는 지 확인 (BitMasking)
deq.append((nr, nc, cnt+1, flag))
visited[nr][nc][flag] = cnt+1
elif miro[nr][nc] == "1": # 달이 차오르는 위치 칸
visited[nr][nc][flag] = min(visited[nr][nc][flag], cnt+1)
elif miro[nr][nc] == "0":
deq.append((nr, nc, cnt+1, flag))
visited[nr][nc][flag] = cnt+1
result = INF
for r in range(N):
for c in range(M):
if miro[r][c] == "1":
result = min(min(visited[r][c]), result)
if result == INF:
print(-1)
else:
print(result)