새소식

PS/백준

[백준/16933] 벽 부수고 이동하기 3 (Python)

  • -

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

 

16933번: 벽 부수고 이동하기 3

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

www.acmicpc.net

Difficulty : Gold 1

Status : Solved (pypy3)

Time : 01:04:26

 


 

 

문제 설명

 

더보기

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을 출력한다.

 

입력 예시

1 4 1
0010

 

출력 예시

5

 

*기타 입출력 예시는 링크를 참조해주세요

 

 


 

 

풀이

BFS 문제 심화판. 특히 파이썬은 시간초과를 겪기 쉽다.

 

경로 탐색 시간을 저장하는 visited 배열은 가로, 세로, 그리고 남은 벽뚫기 횟수를 고려해야 한다(그리고 현재 시간이 낮인지 밤인지에 대해서도 고려하면 확실하지만, 파이썬의 경우 무조건 시간 초과를 겪게 된다).

여기서 포인트는 낮과 밤. 제자리에서 대기해서 시간을 보내는 것이 가능하며, 벽을 뚫는 건 낮에만 가능하다. 즉 다음에 갈 좌표가 벽일 경우에만 낮과 밤을 고려하면 방문의 경우의 수를 크게 줄일 수 있다. 

  • 다음에 갈 좌표가 벽이 아닌 경우 : 이전 방문 시간보다 짧은 경우에 방문 배열을 업데이트하고, 큐에 다음 좌표 정보를 삽입한다.
  • 다음에 갈 좌표가 벽인 경우 :
    • 현재 낮인 경우 : 이전 방문 시간보다 짧은 경우에 방문 배열을 업데이트하고, 큐에 다음 좌표 정보를 삽입한다. 이 때 남은 벽뚫기 횟수를 1 차감하는 것을 잊지 말자.
    • 현재 밤인 경우 : 벽으로 전진할 수 없으므로, 현재 위치에서 대기한다. 즉 큐에 현재 좌표 정보를 삽입한다. 시간은 1 증가시켜야 한다.

 

 

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

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

N, M, K = map(int, input().split())
MAX = float('inf')

board = [list(map(int, input().strip())) for _ in range(N)]
visited = [[[MAX]*(K+1) for _ in range(M)] for _ in range(N)]
visited[0][0][K] = 0
q = deque([(0, 0, 1, K)])
result = MAX

while q :
  x, y, t, left = q.popleft()
  if (x, y) == (M-1, N-1) :
    result = min(result, t)
    continue

  daytime = t % 2
  for k in range(4) :
    ax, ay = x + dx[k], y + dy[k]
    if -1 < ax < M and -1 < ay < N :
      if board[ay][ax] == 0 and visited[ay][ax][left] > t:
        visited[ay][ax][left] = t
        q.append((ax, ay, t+1, left))
      if board[ay][ax] == 1 and left and visited[ay][ax][left-1] > t :
        if daytime : 
          visited[ay][ax][left-1] = t
          q.append((ax, ay, t+1, left-1))
        else :
          q.append((x, y, t+1, left))

print(result if result < MAX else -1)

풀이 완료!

 

골드 문제 상위권 풀이시간이 1시간을 넘나드는데, 좀 더 시간을 단축시키고픈 욕구가 샘솟는 건 어쩔 수 없는 것 같다. 나도 알고리즘 더 잘하고싶당.... ㅠㅠ

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.