🧠 알고리즘
- BFS + 문/열쇠 관리(대기열) + 외곽 진입 탐색
✔️ 동작 방식
[ 핵심 아이디어 ]
- 건물 지도를 탐색하며 **문서($)**를 최대한 많이 수집한다.
- 문(대문자)을 만나면 해당 키(소문자)가 없으면 지나갈 수 없다.
- 키를 얻는 순간, 이전에 막혀 있던 문들을 즉시 열고 탐색을 재개한다.
- 외곽(바깥)에서만 진입 가능하므로, 모든 탐색의 시작 방향은 외곽에서 BFS로 진행한다.
- 방문한 위치는 '#' 로 변경하여 중복 탐색을 방지한다
[ 로직 요약 ]
1) 초기 설정
- 지도 입력 후, 이미 가진 키 목록을 key_info로 저장
- 아직 열 수 없어서 막혀 있는 문들의 좌표를 저장하는 wait_door(dict) 생성
- 외곽(border line)에서 시작 가능한 좌표를 BFS 탐색 시작점으로 등록
2) BFS(now_x, now_y)
- 큐를 이용하여 상하좌우 탐색
- 각 칸에서 다음을 처리:
✔ 문서($)
→ 문서 수 doc 증가
✔ 키(소문자)
→ key_info에 저장
→ 이 키로 열 수 있는 문이 wait_door에 있으면 즉시 큐에 넣어 탐색 재개
✔ 문(대문자)
- 키가 없으면 → wait_door에 좌표 저장하고 패스
- 키가 있으면 → 통과 가능
✔ 빈 공간(.) 또는 이미 열린 통로
→ 그대로 BFS 진행
3) 외곽 탐색
- 벽(*)이 아니고
- 열 수 없는 문이 아니면 → 해당 지점에서 BFS 시작
4) 결과
- 모든 가능한 경로를 탐색한 뒤→ 얻은 문서 수(doc)를 출력
🧾 코드
import sys
from collections import deque
def _main():
doc = 0 # 얻은 문서 갯수
h, w = map(int, sys.stdin.readline().rstrip().split())
map_list = [list(sys.stdin.readline().rstrip()) for _ in range(h)]
key_str = sys.stdin.readline().rstrip()
key_info = set() if key_str == '0' else set(key_str)
# 잠긴 문 위치 저장
wait_door = {}
def _bfs(now_x, now_y):
nonlocal doc
queue = deque([(now_x, now_y)])
if map_list[now_x][now_y].islower(): # 첫 시작이 key 인 경우..
upper_key = map_list[now_x][now_y].upper()
if upper_key in wait_door: # 문 있으면
while wait_door[upper_key]:
go_x, go_y = wait_door[upper_key].popleft()
map_list[go_x][go_y] = '#'
queue.append((go_x, go_y))
map_list[now_x][now_y] = '#'
while queue:
a, b = queue.popleft()
for n_a, n_b in ((a+1, b), (a-1, b), (a, b+1), (a, b-1)):
# 1. 경계 및 벽/방문 여부 검사
if not (0 <= n_a < h and 0 <= n_b < w):
continue
cell_value = map_list[n_a][n_b]
if cell_value in ('*', '#'):
continue
# 2. 문서 획득
if cell_value == '$':
doc += 1
# 3. 키 획득 (소문자)
elif cell_value.islower():
key_info.add(cell_value)
upper_key = cell_value.upper()
# 새로 얻은 키로 현재 wait_door에 잠겨있는 문들을 즉시 엽니다.
if upper_key in wait_door:
# 문들을 큐에 추가하고, wait_door에서 제거합니다.
while wait_door[upper_key]:
go_x, go_y = wait_door[upper_key].popleft()
map_list[go_x][go_y] = '#'
queue.append((go_x, go_y))
# 4. 문 만남 (대문자)
elif cell_value.isupper():
lower_key = cell_value.lower()
if lower_key not in key_info:
# 키가 없으면 wait_door에 저장하고 현재 경로는 탐색 중단
wait_door.setdefault(cell_value, deque()).append((n_a, n_b))
continue
# 5. 문을 통과했거나, 키를 얻었거나, 문서를 얻었거나, 빈 공간을 지나갈 때
map_list[n_a][n_b] = '#'
queue.append((n_a, n_b))
# 외곽 탐색
border_line = []
border_line += [(0, idx) for idx in range(w)]
border_line += [(h-1, idx) for idx in range(w)]
border_line += [(idx, 0) for idx in range(1, h-1)]
border_line += [(idx, w-1) for idx in range(1, h-1)]
for x, y in border_line:
cell_value = map_list[x][y]
if cell_value in ('*', '#'): # 벽이거나 이미 방문 pass..
continue
if cell_value.isupper() and cell_value.lower() not in key_info: # 문 인데.. 열수 없으면..
wait_door.setdefault(cell_value, deque()).append((x, y))
continue
if cell_value in ('.', '$') or cell_value.islower() or (cell_value.isupper() and cell_value.lower() in key_info): # 빈 공간, 문서, 열쇠이면, 문 인데.. 열수 있으면 gogo..
if cell_value == '$':
doc += 1
elif cell_value.islower():
key_info.add(cell_value)
_bfs(x, y)
return doc
case = int(sys.stdin.readline().rstrip())
for _ in range(case):
print(_main())
⏱️ 시간 복잡도
for x, y in border_line : 모든 칸 을 순회 ( 이차형 시간 )
해당 알고리즘 시간 복잡도 : 이차형 시간 복잡도 ( O(n ** 2) )'코딩테스트 파이썬 > 백준' 카테고리의 다른 글
| 좋다 ( 1253 번 ) (0) | 2026.01.01 |
|---|---|
| 수들의 합 5 ( 2018 번 ) (4) | 2025.12.30 |
| 외판원 순회 ( 2098 번 ) (0) | 2025.11.24 |
| 팰린드롬 분할 ( 1509 번 ) (0) | 2025.11.23 |
| 카드 정렬하기 ( 1715 번 ) (0) | 2025.11.14 |