문제 링크
문제 해결
맵에 있는 탐색 가능한 모든 문서의 갯수를 찾는 문제로, BFS를 이용하여 풀었다. 특이한 점은 주어진 열쇠로 맵에 있는 문을 열어서 이동할 수 가 있다는것인데, 열쇠를 맵을 탐색하면서 추가적으로 획득할 수 있기 때문에 이 부분을 유의하여 풀어야 한다.
입력
for _ in range(int(input())):
height, width = map(int, input().split())
floor = [list(input()) for _ in range(height)]
keys = set(input())
visited = [[0 for _ in row] for row in floor]
documents = 0
doors = {} # 열리지 않은 문 리스트
for door in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
doors.setdefault(door, set())
2차원 맵을 입력받기위해 floor
변수에 list comprehension을 이용하여 2중 리스트로 입력받았다.
중복 탐색을 방지하기 위해 floor
와 같은 크기의 맵 변수visited
와 열쇠가 생길 때 열쇠없이 지나간 문을 탐색하기 위한 key가 알파벳 대문자 문, value가 좌표를 담을 set()
인 dictionary doors
변수를 초기화하였다.
탐색
탐색할 경로들 담는 paths
의 리스트의 원소들이 없어질 때까지 반복문을 도는 구조로 기본적인 순회를 구현하였다.
현재 자리에서 상,하,좌,우로 움직여 각 위치에 맞는 문자를 확인한다.
- 벽(
*
): 다음 자리 순회 - 빈 공간(
.
):paths
리스트에 추가 - 문서(
$
): 찾은 문서 숫자를 카운팅 후, 빈 공간(.
)으로 바꿔준 뒤,paths
리스트에 추가한다 - 문(
ABCD...Z
): 열쇠가 있으면,floor
를 빈 공간(.
)으로 바꿔준 뒤,paths
리스트에 추가한다. 열쇠가 없다면 다음에 방문해야 할 문 변수doors
에 현재 좌표를 추가한다 - 열쇠(
abcd...z
): 못 열었던 문들 중 일치하는 열쇠가 있다면paths
에 추가하여 탐색할 수 있도록 한다
코드
from enum import Enum
# move helper
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
class Space(Enum):
EMPTY_SPACE = 1
WALL = 2
DOCUMENT = 3
DOOR = 4
KEY = 5
def checkRange(x: int, y: int):
if x < 0 or x >= width or y < 0 or y >= height:
return False
return True
def checkSpace(what):
if what == '.':
return Space.EMPTY_SPACE
elif what == '*':
return Space.WALL
elif what == '$':
return Space.DOCUMENT
elif 'A' <= what <= 'Z':
return Space.DOOR
elif 'a' <= what <= 'z':
return Space.KEY
def solve(y: int, x: int):
global documents
global doors
paths = [] # discovering paths (queue)
what = floor[y][x]
space = checkSpace(what)
if space == Space.WALL:
return
elif space == Space.DOCUMENT:
documents += 1
floor[y][x] = '.'
elif space == Space.DOOR:
if what.lower() in keys:
floor[y][x] = '.'
else:
doors[what].add((x,y))
return
elif space == Space.KEY:
if what.upper() in doors:
for x,y in doors[what.upper()]:
paths.append({'x': x, 'y': y})
keys.add(floor[y][x].lower())
floor[y][x] = '.'
paths.append({'x': x, 'y': y})
while (len(paths)):
cur = paths.pop()
# 중복 탐색 방지
if visited[cur['y']][cur['x']]:
continue
else:
visited[cur['y']][cur['x']] = 1
for idx in range(4):
curX = cur['x'] + dx[idx]
curY = cur['y'] + dy[idx]
if not checkRange(curX, curY):
continue
# 현재 자리 체크
what = floor[curY][curX]
space = checkSpace(what)
if space == Space.WALL:
continue
elif space == Space.DOCUMENT:
documents += 1
floor[curY][curX] = '.'
paths.append({'x': curX, 'y': curY})
elif space == Space.DOOR:
# 현재 갖고있는 키중에 해당하는 키가 있는지 체크
if what.lower() in keys:
floor[curY][curX] = '.'
visited[curY][curX] = 0
paths.append({'x': curX, 'y': curY})
# 키를 갖고있지 않으면 열리지 않은 문 리스트에 추가
else:
doors[what].add((curX,curY))
elif space == Space.KEY:
if what.upper() in doors:
for x,y in doors[what.upper()]:
paths.append({'x': x, 'y': y})
keys.add(what)
floor[curY][curX] = '.'
visited[curY][curX] = 0
paths.append({'x': curX, 'y': curY})
elif space == Space.EMPTY_SPACE:
paths.append({'x': curX, 'y': curY})
for _ in range(int(input())):
height, width = map(int, input().split())
floor = [list(input()) for _ in range(height)]
keys = set(input())
visited = [[0 for _ in row] for row in floor]
documents = 0
doors = {} # 열리지 않은 문의 좌표를 담을 dictionary 변수
for door in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
doors.setdefault(door, set())
for i in range(height):
for j in range(width):
# visit only edge of the map
if (i == 0 or i == height - 1 or j == 0 or j == width - 1):
solve(i, j)
print(documents)