Problem Solving
[BOJ 13460] 구슬 탈출 2
j_mayo
2022. 7. 24. 01:51
13460번: 구슬 탈출 2
첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'
www.acmicpc.net
처음에 문제를 잘못 봐 구슬이 각각 하나씩 있는 줄 모르고 '이거 무조건 메모리 초과 뜰 거 같은데...' 하면서 2시간을 낭비하고 만 문제이다. (사실 문제를 제대로 본 다음에도 제대로 못 풀었다)
문제 해결을 위한 대략적인 아이디어는 다음과 같다.
- 보드의 상태를 직접 변경하는 것이 아닌, 보드를 확인하며 구슬의 좌표만을 변경하는 것이 핵심이다. 이를 위해 처음 구슬들의 좌표를 확인해서 저장한다.
- 구슬을 움직일 때는 벽('#')과 구멍('O')를 모두 고려해서 움직여야 한다. 한 번에 옮기기보다 루프 내에서 다음 칸을 확인하며 한 칸씩 움직이도록 하는 것이 구현상 편해 그렇게 했다.
- move_marble 함수를 통해 움직이고 난 다음 두 구슬의 좌표가 같은 경우, 더 적게 움직인 구슬이 해당 좌표에 먼저 도달하게 되므로 더 많이 움직인 구슬의 경우 한 칸 되돌려준다.
- queue에는 두 구슬의 좌표와 함께 움직인 횟수가 담긴 tuple을 넣어, 움직인 횟수가 10 이상인 tuple이 나올 시 bfs 특성상 이 이후 모두 움직인 횟수가 10 이상인 tuple만 queue 내부에 있으므로 즉시 while loop를 종료한다.
- 중복된 좌표 방문을 피하기 위해 dictionary 자료형을 활용하였다.
from collections import deque
In = __import__('sys').stdin.readline
N, M = map(int, In().split())
board = [[' '] * M for _ in range(N)]
direction = [(-1, 0), (1, 0), (0, 1), (0, -1)]
cur_coor = [0, 0, 0, 0, 0]
check = {}
for i in range(N):
temp = In().rstrip()
for j in range(M):
board[i][j] = temp[j]
if temp[j] == 'R':
cur_coor[0] = i
cur_coor[1] = j
board[i][j] ='.'
elif temp[j] == 'B':
cur_coor[2] = i
cur_coor[3] = j
board[i][j] = '.'
queue = deque()
queue.append(tuple(cur_coor))
def move_marble(x, y, dir, board):
while board[x+dir[0]][y+dir[1]] != '#' and board[x+dir[0]][y+dir[1]] != 'O':
x += dir[0]
y += dir[1]
return (x, y)
def solution(queue, board, direction, check):
while queue:
temp = queue.popleft()
# 종료 조건: bfs이니 10 이상이 한 번이라도 나오는 순간 종료.
if temp[4] >= 10:
break
for x in range(4):
new_Rx, new_Ry = move_marble(temp[0], temp[1], direction[x], board)
new_Bx, new_By = move_marble(temp[2], temp[3], direction[x], board)
time = temp[-1]
# O에서 멈췄을 경우 파란 구슬도 멈추면 무시, 빨간 구슬만 멈춘 경우 bfs이므로 걸린 시간 return
if board[new_Bx + direction[x][0]][new_By + direction[x][1]] == 'O': continue
if board[new_Rx + direction[x][0]][new_Ry + direction[x][1]] == 'O':
return (time+1)
# 좌표가 같을 경우, 더 짧게 이동한 구슬이 먼저 도달한 것이니
# 더 많이 이동한 구슬을 한 단계 거꾸로 보내준다.
if new_Bx == new_Rx and new_By == new_Ry:
dR = abs(temp[0] - new_Rx + temp[1] - new_Ry)
dB = abs(temp[2] - new_Bx + temp[3] - new_By)
if dR > dB:
new_Rx -= direction[x][0]
new_Ry -= direction[x][1]
else:
new_Bx -= direction[x][0]
new_By -= direction[x][1]
# dict를 visit 확인 용도로 사용한다.
# (dict에서 in은 O(1) 시간)
new_temp = (new_Rx, new_Ry, new_Bx, new_By)
if not new_temp in check:
check[new_temp] = 1
queue.append((new_Rx, new_Ry, new_Bx, new_By, time+1))
# bfs가 그냥 끝난 경우 들어갈 수 없는 경우이니 -1 return
return -1
print(solution(queue, board, direction, check))