코딩테스트

[백준][Python] 14940 쉬운 최단거리

yeeejji 2024. 3. 6. 20:56
728x90

그래프, bfs / 실버 1

🗺️

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

 

14940번: 쉬운 최단거리

지도의 크기 n과 m이 주어진다. n은 세로의 크기, m은 가로의 크기다.(2 ≤ n ≤ 1000, 2 ≤ m ≤ 1000) 다음 n개의 줄에 m개의 숫자가 주어진다. 0은 갈 수 없는 땅이고 1은 갈 수 있는 땅, 2는 목표지점이

www.acmicpc.net

 

내 풀이

from collections import deque

def bfs(x, y):
    q = deque()
    q.append((x,y))

    while q:
        cx, cy = q.popleft()

        for k in range(4):
            nx, ny = cx + dx[k], cy + dy[k]

            if 0 <= nx < n and 0 <= ny < m and ans[nx][ny] == -1: # 범위를 벗어나지 않고, 방문하지 않은 경우
                if land[nx][ny] == 1: # 이동할 수 있는 땅인 경우
                    ans[nx][ny] = ans[cx][cy] + 1 # 이전 값에 +1
                    q.append((nx, ny))

n, m = map(int, input().split())
land = [[] * m for _ in range(n)]
ans = [[-1] * m for _ in range(n)]

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

for i in range(n):
    land[i] = list(map(int, input().split()))

for i in range(n):
    for j in range(m):
        if land[i][j] == 0:
            ans[i][j] = 0
        elif land[i][j] == 2:
            # 목표 지점 좌표
            targetX = i
            targetY = j

ans[targetX][targetY] = 0
bfs(targetX, targetY)

for i in range(len(ans)):
    print(*ans[i])

bfs(너비 우선 탐색)으로 풀었다.

 

 

코드 설명

 

ans: 정답 리스트

전체 리스트를 -1로 초기화했다. 

-1은 방문하지 않은 땅임을 의미한다.

후에 해당 땅을 방문하게 되면 양의 정수로 대체될 것이고, 도달할 수 없는 위치인 경우에는 -1이 그대로 유지될 것이다. 

ans = [[-1] * m for _ in range(n)]

 

가로와 세로로만 움직일 수 있다 ➡ 상하좌우로 이동할 좌표를 리스트에 저장해야 한다.

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

 

모든 땅을 돌면서 방문할 수 없는 땅과 목표 지점을 알아냈다.

방문할 수 없는 땅을 만났다면, 정답 리스트에도 똑같이 반영해야 한다.

for i in range(n):
    for j in range(m):
        if land[i][j] == 0:
            ans[i][j] = 0
        elif land[i][j] == 2:
            # 목표 지점 좌표
            targetX = i
            targetY = j

 

목표 지점의 좌표에서는 목표 지점까지의 거리가 0이기 때문에 0으로 치환한다.

목표 지점의 좌표를 매개변수로 넣어 bfs를 호출한다.

ans[targetX][targetY] = 0
bfs(targetX, targetY)

 

 

✔️ bfs 함수 내

 

범위를 벗어나지 않고, 방문하지 않은 땅인 경우이며, 이동할 수 있는 땅인 경우라면

이전 값에 1을 더해준 뒤(거리를 1 증가시킨 뒤), deque에 좌표를 추가한다.

if 0 <= nx < n and 0 <= ny < m and ans[nx][ny] == -1: # 범위를 벗어나지 않고, 방문하지 않은 경우
    if land[nx][ny] == 1: # 이동할 수 있는 땅인 경우
        ans[nx][ny] = ans[cx][cy] + 1 # 이전 값에 +1
        q.append((nx, ny))

 

 

어려웠던 점

 

갈 수 없는 땅(0)을 다루는 과정에서 난항이 있었다.

처음에는 다음과 같이 bfs 함수 내에서 이동할 수 있는 땅인 경우와 이동할 수 없는 땅인 경우를 나누어 함께 처리했었는데,

틀렸다.

if 0 <= nx < n and 0 <= ny < m and ans[nx][ny] == -1: # 범위를 벗어나지 않고, 방문하지 않은 경우
    if land[nx][ny] == 1: # 이동할 수 있는 땅인 경우
        ans[nx][ny] = ans[cx][cy] + 1 # 이전 값에 +1
        q.append((nx, ny))
        
    # 처음에 처리했던 방식
    elif land[nx][ny] == 0: # 이동할 수 없는 땅인 경우
        ans[nx][ny] = 0

 

위의 코드를 살펴보면,

이동할 수 있는 땅인 경우에만 deque에 좌표를 추가해주고 있다.

즉, 이동할 수 없는 땅에서는 상하좌우로 이동할 기회가 주어지지 않는다는 것이다. 

다음과 같이 이동할 수 없는 땅이 한 겹인 경우에는 문제가 발생하지 않는다.

 

문제가 발생하는 케이스는, 다음과 같이 이동할 수 없는 땅이 여러 겹으로 주어졌을 때이다.

 

이동할 수 없는 땅인 0에서는 상하좌우로 이동할 기회가 주어지지 않기 때문에,

노란색 박스의 값들이 검사되지 않고 함수가 종료되게 된다.

 

따라서 이동할 수 없는 땅을 정답 리스트에 똑같이 반영해주는 과정을 먼저 수행하고 나서 bfs를 호출해야 한다!

 

뭐가 잘못됐는지 모르겠어서, 구글링 해볼까하다가 조금 더 생각해 보고 결국 스스로 해결했다. 뿌듯 ✌🏻