[백준] 16933번 벽 부수고 이동하기 3 - Python / 알고리즘 중급 1/3 - BFS (연습)

ByungJik_Oh·2025년 5월 20일
0

[Baekjoon Online Judge]

목록 보기
147/244
post-thumbnail



💡 문제

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다. 이동하지 않고 같은 칸에 머물러있는 경우도 가능하다. 이 경우도 방문한 칸의 개수가 하나 늘어나는 것으로 생각해야 한다.

이번 문제에서는 낮과 밤이 번갈아가면서 등장한다. 가장 처음에 이동할 때는 낮이고, 한 번 이동할 때마다 낮과 밤이 바뀌게 된다. 이동하지 않고 같은 칸에 머무르는 경우에도 낮과 밤이 바뀌게 된다.

만약에 이동하는 도중에 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 K개 까지 부수고 이동하여도 된다. 단, 벽은 낮에만 부술 수 있다.

한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000), K(1 ≤ K ≤ 10)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

출력

첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.


💭 접근

이 문제의 경우 14442번 벽 부수고 이동하기 2 문제와 매우 유사하지만 벽을 항상 부술 수 있는 것이 아닌 낮에만 벽을 부술 수 있다는 조건이 추가되었다.

이전 문제와 마찬가지로 벽을 k개 부술 수 있다는 점에서 visited 리스트를 2차원이 아닌 3차원 리스트를 사용해야 하는 것은 동일하지만, 처음 이 문제를 접했을 때는 낮, 밤도 구분하기 위해 n x m x k x 2 4차원 리스트를 사용하였지만 계속해서 시간 초과가 발생하였고, 해결방법을 찾아본 결과 많은 것을 배울 수 있었다.

우선 아래 코드와 같이 3차원 리스트를 사용하고, bfs함수에서 day 변수를 이용하여 낮, 밤 상태를 전달해주는 것까지는 맞았지만,

  1. 기존의 bfs 함수를 구현할 때처럼 main문이 아닌 따로 함수를 선언하면 함수를 호출할 때 발생하는 오버헤드로 인해 시간이 지연될 수 있다는 점이다.
  2. 또한, pypy3의 경우 int 자료형을 사용하는 것보다 bool 자료형을 사용하는 것이 훨 씬 빠르다는 점이다.

이 두가지를 고려하여 기존의 코드에서 bool자료형을 사용했던 visitedday변수를 0과 1로, main 문과 따로 구현했던 bfs함수를 main문과 합치니 시간초과가 해결되었다.


📒 코드

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

n, m, k = map(int, input().split())
graph = [list(map(int, input().rstrip())) for _ in range(n)]
visited = [[[0 for _ in range(m)] for _ in range(n)] for _ in range(k + 1)]

q = deque()
q.append((0, 0, 0, 1, 1))

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

while q:
    x, y, wall, cnt, day = q.popleft()

    if x == n - 1 and y == m - 1:
        print(cnt)
        sys.exit()

    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]

        if 0 <= nx < n and 0 <= ny < m:
            if day == 1:
                if wall < k:
                    if graph[nx][ny] == 0 and visited[wall][nx][ny] == 0:
                        visited[wall][nx][ny] = 1
                        q.append((nx, ny, wall, cnt + 1, 0))
                    elif graph[nx][ny] == 1 and visited[wall + 1][nx][ny] == 0:
                        visited[wall + 1][nx][ny] = 1
                        q.append((nx, ny, wall + 1, cnt + 1, 0))
                if wall == k:
                    if graph[nx][ny] == 0 and visited[wall][nx][ny] == 0:
                        visited[wall][nx][ny] = 1
                        q.append((nx, ny, wall, cnt + 1, 0))
            else:
                if graph[nx][ny] == 0 and visited[wall][nx][ny] == 0:
                    visited[wall][nx][ny] = 1
                    q.append((nx, ny, wall, cnt + 1, 1))
                elif graph[nx][ny] == 1:
                    q.append((x, y, wall, cnt + 1, 1))

print(-1)

💭 후기

시간제한이 매우 빡빡한 문제의 경우 위 내용과 같은 부분으로도 정답유무가 갈릴 수 있다는 것을 배웠고, 시간초과 발생 시 이러한 점들까지 고려해 보아야겠다.


🔗 문제 출처

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


profile
精進 "정성을 기울여 노력하고 매진한다"

0개의 댓글