세상을 바꾸는 데이터

[백준 16954번] 움직이는 미로 탈출 - 파이썬 본문

PS Study/BOJ(백준)

[백준 16954번] 움직이는 미로 탈출 - 파이썬

Industriousness 2022. 3. 6. 11:01


문제 링크:

https://www.acmicpc.net/problem/16954

 

16954번: 움직이는 미로 탈출

욱제는 학교 숙제로 크기가 8×8인 체스판에서 탈출하는 게임을 만들었다. 체스판의 모든 칸은 빈 칸 또는 벽 중 하나이다. 욱제의 캐릭터는 가장 왼쪽 아랫 칸에 있고, 이 캐릭터는 가장 오른쪽

www.acmicpc.net



풀이 유형:

그래프 이론, 그래프 탐색, 너비 우선 탐색

 

문제풀이 Point

1. 계속해서 이동하는 벽을 어떻게 추가하고 삭제하는지 고민 필요(set, pop, appendleft)

2. 캐릭터가 이동한 다음, 벽이 이동하는 순서로 진행

3. 너비 우선 탐색(bfs)으로 구현

 

풀이 과정:

이 문제는 욱제의 캐릭터가 가장 오른쪽 윗 칸으로 이동할 수 있는지 없는지 구해보는 문제이다.

bfs(너비 우선 탐색)으로 풀면 되는데, 벽을 어떻게 없애고 새로 생성하는지 고민을 해봐야 한다.

첫 번째 방법:

벽이 있는 위치를 집합으로 받고, 1초씩 지날 때마다 벽의 위치를 업데이트한다.

두 번째 방법:

맵에 있는 맨 마지막 행을 삭제하고, 맨 위 행에 빈칸으로 이루어진 행을 삽입한다.

 

16954 파이썬 첫 번째 방법 결과
16954 파이썬 두 번째 방법 결과

 

첫 번째 방법이 두 번째 방법보다 변수와 조건문을 자주 사용하지 않은 측면에서 시간 복잡도와 메모리가 작은 코드로 나타났다.

 

풀이 코드:

 

첫 번째 방법: 벽이 있는 위치를 집합으로 받고, 1초씩 지날 때마다 벽의 위치를 업데이트

# 16954번 움직이는 미로 탈출

# 벽의 위치를 받아 해결

import sys
input = sys.stdin.readline
from collections import deque

# 맵 리스트, 벽 위치 집합, 정답 변수 생성
board, wall, answer = [input().rstrip() for _ in range(8)], set(), 0
# 벽 위치 추가
for i in range(8):
    for j in range(8):
        if board[i][j] == '#':
            wall.add((i, j))
# 9가지 방향 정의
steps = [[0, 0], [-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]]
# 방문 표시 집합 생성
visited = set()
# 큐 정의
q = deque()
q.append((7, 0))

# 큐가 빌 때까지 반복
while q:
    for _ in range(len(q)):
        x, y = q.popleft()
        # 현재 위치에 벽이 있다면 건너뛰기
        if (x, y) in wall:
            continue
        # 현재 위치가 도착 지점이라면
        if x == 0 and y == 7:
            # 1이 정답
            answer = 1
            # 반복문 탈출
            break
        # 이동할 수 있는 모든 방향에 대하여
        for dx, dy in steps:
            nx = x + dx
            ny = y + dy
            # 조건을 만족한다면
            if 0 <= nx <= 7 and 0 <= ny <= 7 and not (nx, ny) in visited and not (nx, ny) in wall:
                # 방문 표시 추가
                visited.add((nx, ny))
                # 큐에 추가
                q.append((nx, ny))
    # 벽이 있다면 방문 표시 초기화
    if wall:
        visited = set()
    # 다음 위치에 있는 벽 집합 생성
    next_wall = set()
    # 다음 벽의 위치 추가
    for x, y in wall:
        if x < 7:
            next_wall.add((x+1, y))
    # 벽 위치를 업데이트
    wall = next_wall

print(answer)

 

두 번째 방법: 맵에 있는 맨 마지막 행을 삭제하고, 맨 위 행에 빈칸으로 이루어진 행을 삽입

# 16954번 움직이는 미로 탈출

# 1. 맵 입력받기
# 2. bfs 구현

from collections import deque

# bfs 함수 구현
def bfs():
    # 시작 위치 삽입
    q = deque([(7, 0)])
    # 벽 이동 횟수
    turn = 0
    # 큐가 빌 때까지 반복
    while q:
        # 큐에 있는 위치 개수
        len_q = len(q)
        for _ in range(len_q):
            # 행과 열 꺼내기
            row, col = q.popleft()
            # 시작 위치가 벽이라면 건너뛰기
            if map[row][col] == '#':
                continue
            # 시작 위치가 도착 지점이면 1을 반환
            if (row, col) == (0, 7):
                return 1
            # 9가지 이동 방향에 대하여
            for i in range(9):
                nx = row + dx[i]
                ny = col + dy[i]
                # 이동 위치가 맵을 벗어난다면
                if not (0 <= nx <= 7 and 0 <= ny <= 7):
                    continue
                # 이동 위치가 빈 곳이라면
                if map[nx][ny] == '.':
                    q.append((nx, ny))
        # 맵 가장 아래에 있는 행 삭제
        map.pop()
        # 맵 가장 위에 빈 칸으로 이루어진 행을 삽입
        map.appendleft(['.', '.', '.', '.', '.', '.', '.', '.'])
        # 벽 이동 횟수 추가
        turn += 1
        # 벽 이동 횟수가 9가 되면 모든 벽이 사라지므로
        if turn == 9:
            return 1
    # 캐릭터가 더 이상 움직일 수 없다면 0을 반환
    return 0

# 맵 입력받기
map = deque(list(input()) for _ in range(8))
# 캐릭터가 이동할 수 있는 방향: 정지, 상, 하, 좌, 우, 대각선(왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래)
dx = [0, -1, 1, 0, 0, -1, -1, 1, 1]
dy = [0, 0, 0, -1, 1, -1, 1, -1, 1]
# bfs 함수 실행 후 정답 출력
print(bfs())

 

 

 

16954 움직이는 미로 탈출

 

728x90
반응형
Comments