Solution
Python
from collections import deque
def check_move(n, m, y, x, log, maze):
# maze 범위 벗어나는지 체크
if not ((0 <= y < n) & (0 <= x < m)):
return False
# 이미 지나온 위치인지 체크
if log[y][x]:
return False
# 진행 방향에 벽이 있는지 체크
if (maze[y][x] == 5):
return False
# 모든 경우 다 되면 True
return True
def move(n, m, y, x, log, maze):
# 수레가 갈 수 있는 방향 / 상하좌우
direction = [(-1,0), (1,0), (0,-1), (0,1)]
# 이동 가능한 좌표 값은 리스트에 저장
result = []
# 4방향으로 수레를 이동시킬 수 있는지 체크
for i in range(4):
dy, dx = direction[i][0], direction[i][1]
# check_move 함수를 통해 체크하고
if check_move(n, m, y+dy, x+dx, log, maze):
# 유효한 위치라면 result에 추가
result.append((y+dy, x+dx))
# result를 반환해준다.
return result
def bfs(maze, ry, rx, by, bx, n, m):
# 최단 횟수 저장할 변수
answer = 0
# 방문용 배열 만들기 (ry, rx, by, bx)
visited = [[[[False]*m for _ in range(n)] for _ in range(m)] for _ in range(n)]
# 최근 기록 배열 만들기
log_red = [[False] * m for _ in range(n)]
log_blue = [[False] * m for _ in range(n)]
# 스타트 위치는 방문으로 표시해준다.
log_red[ry][rx] = True
log_blue[by][bx] = True
# BFS 탐색으로 최단 횟수를 알기 위해, Queue 생성
Q = deque()
Q.append((ry, rx, by, bx, log_red, log_blue, 0))
# BFS 탐색 시작
while Q:
# Q의 값을 꺼내온다.
ry, rx, by, bx, rV, bV, cnt = Q.popleft()
# 방문한 장소라면 패스
if visited[ry][rx][by][bx]: continue
# 아니라면 방문 체크
visited[ry][rx][by][bx] = True
# 빨간 수레, 파란 수레 도착지점 체크 변수
chk_r, chk_b = False, False
# 빨간 수레가 도착지점 도착 시 True
if maze[ry][rx] == 3: chk_r = True
# 파란 수레가 도착지점 도착 시 True
if maze[by][bx] == 4: chk_b = True
# 둘 다 도착지점 도착 완료했다면,
if chk_r & chk_b:
# answer에 현재까지 카운트 저장 후 탈출
answer = cnt
break
# 빨간 수레, 파란 수레 둘 다 도착 못 함
elif ((not chk_r) & (not chk_b)):
# 빨간 수레 유효 좌표 체크
nrs = move(n, m, ry, rx, rV, maze)
# 파란 수레 유효 좌표 체크
nbs = move(n, m, by, bx, bV, maze)
# 빨간 수레 도착, 파란 수레 미도착
elif (chk_r & (not chk_b)):
# 파란 수레 유효 좌표 체크
nbs = move(n, m, by, bx, bV, maze)
# 빨간 수레는 현위치 고정!
nrs = [(ry, rx)]
# 빨간 수레 미도착, 파란 수레 도착
elif ((not chk_r) & chk_b):
# 빨간 수레 유효 좌표 체크
nrs = move(n, m, ry, rx, rV, maze)
# 파란 수레는 현위치 고정!
nbs = [(by, bx)]
# 빨간 수레 이동 가능 위치와 파란 수레 이동 가능 위치 탐색
for nry, nrx in nrs:
for nby, nbx in nbs:
# 현재 이동한 위치의 두 수레가 겹치면 패스
if ((nry == nby) & (nrx == nbx)): continue
# 만약 서로 이동한 위치가 이전 위치를 바꾼거라면 패스(겹치는 이동)
if (((nry==by)&(nrx==bx))and((nby==ry)&(nbx==rx))): continue
# 걸리는 부분 없이 이동가능하다면, 해당 위치로 옮겨준다.
cnt += 1
# 해당 위치 방문 체크를 해준다.
rV[nry][nrx], bV[nby][nbx] = True, True
# Q 에 해당위치들을 넣어준다.
Q.append((nry, nrx, nby, nbx, rV, bV, cnt))
# for 문 내에서 다음 위치들 확인할 때 누적연산 되므로
cnt -= 1
rV[nry][nrx], bV[nby][nbx] = False, False
# answer 그대로 return
return answer
def solution(maze):
n = len(maze) # 세로 길이
m = len(maze[0]) # 가로 길이
# 수레들 스타트 위치 찾기
for y in range(n):
for x in range(m):
# 빨간 수레 위치 저장
if maze[y][x] == 1:
ry, rx = y, x
# 해당 위치는 빈칸으로 초기화
maze[y][x] = 0
# 파란 수레 위치 저장
elif maze[y][x] == 2:
by, bx = y, x
# 해당 위치는 빈칸으로 초기화
maze[y][x] = 0
# BFS 탐색 함수에 넣고, 최단 횟수를 answer에 저장
answer = bfs(maze, ry, rx, by, bx, n, m)
return answer
문제 설명
n
x m
크기 격자 모양의 퍼즐판이 주어집니다.
퍼즐판에는 빨간색 수레와 파란색 수레가 하나씩 존재합니다. 각 수레들은 자신의 시작 칸에서부터 자신의 도착 칸까지 이동해야 합니다.
모든 수레들을 각자의 도착 칸으로 이동시키면 퍼즐을 풀 수 있습니다.
당신은 각 턴마다 반드시 모든 수레를 상하좌우로 인접한 칸 중 한 칸으로 움직여야 합니다. 단, 수레를 움직일 때는 아래와 같은 규칙이 있습니다.
- 수레는 벽이나 격자 판 밖으로 움직일 수 없습니다.
- 수레는 자신이 방문했던 칸으로 움직일 수 없습니다.
- 자신의 도착 칸에 위치한 수레는 움직이지 않습니다. 계속 해당 칸에 고정해 놓아야 합니다.
- 동시에 두 수레를 같은 칸으로 움직일 수 없습니다.
- 수레끼리 자리를 바꾸며 움직일 수 없습니다.
예를 들어, 아래 그림처럼 n
= 3, m
= 2인 퍼즐판이 있습니다.
- 속이 빨간색인 원은 빨간색 수레를 나타냅니다.
- 속이 파란색인 원은 파란색 수레를 나타냅니다.
- 테두리가 빨간색인 원은 빨간색 수레의 도착 칸을 나타냅니다.
- 테두리가 파란색인 원은 파란색 수레의 도착 칸을 나타냅니다.
위 퍼즐판은 아래와 같은 순서로 3턴만에 풀 수 있습니다.
- 빨간색 사선이 처진 칸은 빨간색 수레가 방문했던 칸을 나타냅니다. 규칙에 따라 빨간색 수레는 빨간색 사선이 처진 칸(방문했던 칸)으로는 이동할 수 없습니다.
- 파란색 사선이 처진 칸은 파란색 수레가 방문했던 칸을 나타냅니다. 규칙에 따라 파란색 수레는 파란색 사선이 처진 칸(방문했던 칸)으로는 이동할 수 없습니다.
- 위처럼 동시에 수레를 같은 칸으로 움직일 수는 없습니다.
퍼즐판의 정보를 나타내는 2차원 정수 배열 maze
가 매개변수로 주어집니다. 퍼즐을 푸는데 필요한 턴의 최솟값을 return 하도록 solution 함수를 완성해 주세요. 퍼즐을 풀 수 없는 경우 0을 return 해주세요.
제한사항
- 1 ≤
maze
의 길이 (= 세로 길이) ≤ 4- 1 ≤
maze[i]
의 길이 (= 가로 길이) ≤ 4 maze[i][j]
는 0,1,2,3,4,5 중 하나의 값을 갖습니다.
- 1 ≤
maze[i][j] |
의미 |
---|---|
0 | 빈칸 |
1 | 빨간 수레의 시작 칸 |
2 | 파란 수레의 시작 칸 |
3 | 빨간 수레의 도착 칸 |
4 | 파란 수레의 도착 칸 |
5 | 벽 |
- 빨간 수레의 시작 칸, 빨간 수레의 도착 칸, 파란 수레의 시작 칸, 파란 수레의 도착 칸은 퍼즐판에 1개씩 존재합니다.
입출력 예
maze | result |
---|---|
[[1, 4], [0, 0], [2, 3]] | 3 |
[[1, 0, 2], [0, 0, 0], [5, 0 ,5], [4, 0, 3]] | 7 |
[[1, 5], [2, 5], [4, 5], [3, 5]] | 0 |
[[4, 1, 2, 3]] | 0 |
입출력 예 설명
입출력 예 #1
문제 예시와 같습니다.
입출력 예 #2
7턴만에 퍼즐을 풀 수 있습니다. 다른 방법으로도 퍼즐을 풀 수 있지만 7턴보다 빠르게 풀 수는 없습니다.
입출력 예 #3
다음 턴에 파란색 수레가 파란색 수레의 도착 칸에 위치한 후 고정되어 빨간색 수레가 빨간색 수레의 도착 칸에 도착할 수 없게 됩니다.
퍼즐을 풀 수 없으므로 0을 return 해야 합니다.
입출력 예 #4
수레는 서로 위치를 바꾸면서 움직일 수 없으므로 퍼즐을 풀 수 없습니다. 따라서 0을 return 해야 합니다.