프로그래머스-블록 이동하기

업데이트:

문제

블록 이동하기 (2020카카오공채)

풀이

문제조건 해석

로봇은 왼쪽, 오른쪽 발 있다.

이동 / 회전 조건에 따라서 블록을 움직이면서 목표 위치까지 도달하는 최소시간 구하기

  • 이동 조건 : 상하좌우 이동 가능, 왼쪽 또는 오른쪽 발에 벽이 있으면 이동 불가능
  • 회전 조건 : 90도 회전 가능, 회전 결과에 벽이 있거나 회전 중에 벽에 부딪히면 이동 불가능
    • 왼쪽 축을 기준으로 90도 회전하는 2가지 경우 존재
    • 오른쪽 축을 기준으로 90도 회전하는 2가지 경우 존재

알고리즘

BFS + 복잡한 조건 이용해서 구현했다.

왼쪽 축과 오른쪽 축을 구분해서 회전시켜주었는데, 양 축이 조건에 맞게 지정되었는지 확인해 주어야 한다.

  • 왼쪽 축은 항상 오른쪽 축보다 왼쪽에 있거나, 아래에 있어야 함

코드

from collections import deque


def bfs(board):
    n = len(board)
    queue = deque([((0, 0), (0, 1))])
    visited = {((0, 0), (0, 1))}
    time = 0
    while queue:
        for _ in range(len(queue)):
            left, right = queue.popleft()
            li, lj = left
            ri, rj = right
            if (li, lj) == (n - 1, n - 1) or (ri, rj) == (n - 1, n - 1):
                return time
            # 이동
            for di, dj in [(-1, 0), (0, -1), (1, 0), (0, 1)]:
                if not (0 <= li + di < n and 0 <= ri + di < n and
                        0 <= lj + dj < n and 0 <= rj + dj < n):
                    continue
                if not (board[li + di][lj + dj] or board[ri + di][rj + dj]) and \
                        ((li + di, lj + dj), (ri + di, rj + dj)) not in visited:
                    visited.add(((li + di, lj + dj), (ri + di, rj + dj)))
                    queue.append(((li + di, lj + dj), (ri + di, rj + dj)))
            # 회전
            # 가로로 위치해 있을때
            if li == ri:
                if lj > rj:  # 왼쪽 축이 오른쪽 축보다 오른쪽에 있다면
                    li, lj, ri, rj = ri, rj, li, lj
                # 왼쪽 축으로 해서 회전
                for di, dj in [(-1, -1), (1, -1)]:
                    if not (0 <= ri + di < n and 0 <= rj + dj < n):
                        continue
                    if board[ri + di][rj]:  # 회전 중 벽에 충돌
                        continue
                    if not board[ri + di][rj + dj] and ((li, lj), (ri + di, rj + dj)) not in visited:
                        visited.add(((li, lj), (ri + di, rj + dj)))
                        queue.append(((li, lj), (ri + di, rj + dj)))
                # 오른쪽 축으로 회전
                for di, dj in [(-1, 1), (1, 1)]:
                    if not (0 <= li + di < n and 0 <= lj + dj < n):
                        continue
                    if board[li + di][lj]:
                        continue
                    if not board[li + di][lj + dj] and ((li + di, lj + dj), (ri, rj)) not in visited:
                        visited.add(((li + di, lj + dj), (ri, rj)))
                        queue.append(((li + di, lj + dj), (ri, rj)))
            # 세로로 위치해 있을 때
            elif lj == rj:
                if li < ri:  # 왼쪽 축이 오른쪽 축보다 위쪽에 위치한다면
                    li, lj, ri, rj = ri, rj, li, lj
                # 아래 축으로 해서 회전
                for di, dj in [(1, -1), (1, 1)]:
                    if not (0 <= ri + di < n and 0 <= rj + dj < n):
                        continue
                    if board[ri][rj + dj]:  # 회전 중 벽에 충돌
                        continue
                    if not board[ri + di][rj + dj] and ((li, lj), (ri + di, rj + dj)) not in visited:
                        visited.add(((li, lj), (ri + di, rj + dj)))
                        queue.append(((li, lj), (ri + di, rj + dj)))
                # 위쪽 축으로 회전
                for di, dj in [(-1, -1), (-1, 1)]:
                    if not (0 <= li + di < n and 0 <= lj + dj < n):
                        continue
                    if board[li][lj + dj]:
                        continue
                    if not board[li + di][lj + dj] and ((li + di, lj + dj), (ri, rj)) not in visited:
                        visited.add(((li + di, lj + dj), (ri, rj)))
                        queue.append(((li + di, lj + dj), (ri, rj)))
        time += 1


def solution(board):
    return bfs(board)

배운점

  • 무작정 조건에 따라 분기하면 예외 상황 발생하기 쉽다.
  • 디버깅은 시간이 더 소요되니 처음에 잘 생각해서 코딩하자.

댓글남기기