220301 - 벽 부수고 이동하기

이상해씨·2022년 3월 1일
0

알고리즘 풀이

목록 보기
54/94

◾ 벽 부수고 이동하기 : 백준 2206번

문제

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

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

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

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


입력

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

출력

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

입출력 예

InputOutput
6 4
0100
1110
1000
0000
0111
0000
15
4 4
0111
1111
1111
1110
-1

◾ 풀이

1. 해설

  • 동적 계획법을 통해 해결할 수 있다.
  • 일반적인 동적 계획법과 달리 [벽을 부순다]라는 조건을 추가로 변수로 둔다.
    • (r, c, w) : r(행), c(열), w(벽 부숨 여부)로 방문 여부 표현
    • 벽을 부수지 않은 경우 w를 0, 벽을 부순 경우 w를 1로 설정
    • BFS를 통해 최단 거리를 찾는다.
    • 같은 좌표라도 벽은 부순 경우와 부수지 않은 경우는 다른 좌표로 생각한다.

2. 프로그램

  1. n, m, board 입력
  2. visited, answer 초기화
  3. 덱을 사용해 초기 좌표 설정 (0, 0, 1, 0) : (r, c, cnt, w)
  4. BFS 알고리즘을 통해 최단 거리 탐색
    • (r, c, cnt, w) : 행, 열, 이동수, 벽 이동
    • 다음 좌표 값이 0인 경우 벽 부숨과 상관없이 이동
    • 다음 좌표 값이 1인 경우 w가 0인 경우만 이동
  5. 최종 좌표에 도달하는 경우 cnt 반환, 도달하지 못하는 경우 -1 반환
# 코드
from collections import deque

n, m = map(int, input().split(' '))
board = [list(map(int, input())) for _ in range(n) ]
visited = {}
answer = -1

points = deque([])
# 좌표 r(행), 좌표 c(열), 이동 수, 벽 이동
points.append((0, 0, 1, 0))
# 좌표 r(행), 좌표 c(열), 벽 이동
visited[(0, 0, 0)] = True

end_r, end_c = n-1, m-1
while points:
    r, c, cnt, wall = points.popleft()
    # 최종 좌표일 경우 값 반환 및 종료
    if r == end_r and c == end_c:
        answer = cnt
        break
    # 상, 하, 좌, 우 탐색
    for d_r, d_c in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
        new_r, new_c = r + d_r, c + d_c

        # 범위를 넘억가는 경우 패스
        if new_r < 0 or new_r >=n or new_c <0 or new_c >= m:
            continue
        # 길인 경우 탐색
        if board[new_r][new_c] == 0 and (new_r, new_c, wall) not in visited:
            points.append((new_r, new_c, cnt+1, wall))
            visited[(new_r, new_c, wall)] = True
        # 벽인 경우 벽을 부수고 이동
        # 단, 벽을 이미 부순 경우는 이동할 수 없다.
        if board[new_r][new_c] == 1 and wall == 0:
            points.append((new_r, new_c, cnt+1, 1))
            visited[(new_r, new_c, 1)] = True

print(answer)
  • Input
    6 4
    0100
    1110
    1000
    0000
    0111
    0000

  • Input
    4 4
    0111
    1111
    1111
    1110

profile
후라이드 치킨

0개의 댓글

관련 채용 정보