문제
NXM 크기의 보드와 4개의 버튼으로 이루어진 게임이 있습니다. 보드는 1X1 크기의 정사각형 칸으로 나누어져 있습니다. 각각 칸은 비어있거나 벽입니다. 두개의 빈칸에는 동전이 하나씩 놓여져있고 두 동전의 위치는 다릅니다.
버튼은 왼쪽/오른쪽/위/아래와 같이 4가지가 있습니다.
버튼을 누르면 두 동전은 동시에 버튼에 쓰여져있는 방향으로 이동합니다.
한 동전만을 보드에서 떨어뜨리기 위해 최소한으로 버튼을 몇번 눌러야하는지를 구하는 문제입니다.
제약조건
풀이
BFS 알고리즘을 이용했습니다. queue에는 최소한으로 이동했을때, 이동가능한 두 동전의 발견된 위치들을 저장합니다.
queue의 크기 단위로 버튼 누르는 횟수를 셉니다.
다음과 같은 경우 queue에 집어넣지 않습니다.
queue의 크기가 0이 되거나 버튼 누른횟수가 10을 넘어갈경우, -1을 반환합니다.
코드
'''
Created by jun on 21/05/25
'''
import collections
buttons = [(0, -1), (0, 1), (-1, 0), (1, 0)] #y, x
N, M = map(int, input().split())
board = [list(input()) for _ in range(N)]
coins = list()
for y in range(N):
for x in range(M):
if board[y][x] == 'o':
coins.append((y, x))
# ((3, 0), (4, 0))
def bfs(coins):
discovered = set()
discovered.add(coins)
queue = collections.deque()
queue.append(coins)
cnt = 0
while queue and cnt < 10:
cnt += 1
for _ in range(len(queue)):
coin1, coin2 = queue.popleft()
for button in buttons:
dropped_coin = 0
#새로운 동전의 위치
new_coin1 = (coin1[0] + button[0], coin1[1] + button[1])
new_coin2 = (coin2[0] + button[0], coin2[1] + button[1])
#동전 위치 조정
#동전 1이 떨어진 경우 / 범위를 벗어난 경우.
if not(0 <= new_coin1[0] < N and 0 <= new_coin1[1] < M):
dropped_coin += 1
#동전 1이 #인경우
#버튼 조작을 취소한다.
elif board[new_coin1[0]][new_coin1[1]] == '#':
new_coin1 = (coin1[0], coin1[1])
#동전 2가 떨어진경우
if not(0 <= new_coin2[0] < N and 0 <= new_coin2[1] < M):
dropped_coin += 1
# 동전 2이 #인경우
# 버튼 조작을 취소한다.
elif board[new_coin2[0]][new_coin2[1]] == '#':
new_coin2 = (coin2[0], coin2[1])
# 예외처리 : 이미 방문한 경우 / 안 움직인 경우
# 모든 조작이 완료된 상태
if (new_coin1, new_coin2) in discovered:
continue
#하나도 안떨어진 경우 -> 재평가
if dropped_coin == 0:
queue.append((new_coin1, new_coin2))
discovered.add((new_coin1, new_coin2))
#하나만 떨어진경우 -> 정답
elif dropped_coin == 1:
return cnt
#둘다 떨어진경우 -> 평가 종료.
return -1
print(bfs(tuple(coins)))
새로 알게된 사실
판에서 동전을 이동시키는 법
move 함수를 선언해서 이동한 y,x를 반환하면 깔끔하게 짤수있다. (이동 불가할땐 -1 반환)