본문 바로가기

IT/Python

[백준] 11657번 타임머신 [Python] - 벨만 포드

728x90

n개의 도시가 있고, 1번 도시에서 나머지 도시로 가는 최소 시간을 구하는 문제

m개의 버스 노선이 시작도시, 도착도시, 시간 정보로 주어진다.

시간은 양수, 0$($순간 이동$)$, 음수$($타임머신$)$일 수 있다.

 

소요 시간을 음의 무한대로 향하게 할 수 있다면 -1을 출력한다.

그게 아니라면 순서대로 소요시간을 출력하고, 해당 도시로 가는 경로가 없는 경우는 시간 대신 -1을 출력한다.

 

더보기

문제

N개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 버스가 M개 있다. 각 버스는 A, B, C로 나타낼 수 있는데, A는 시작도시, B는 도착도시, C는 버스를 타고 이동하는데 걸리는 시간이다. 시간 C가 양수가 아닌 경우가 있다. C = 0인 경우는 순간 이동을 하는 경우, C < 0인 경우는 타임머신으로 시간을 되돌아가는 경우이다.

1번 도시에서 출발해서 나머지 도시로 가는 가장 빠른 시간을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 도시의 개수 N $(1 ≤ N ≤ 500)$, 버스 노선의 개수 M $(1 ≤ M ≤ 6,000)$이 주어진다. 둘째 줄부터 M개의 줄에는 버스 노선의 정보 A, B, C $(1 ≤ A, B ≤ N, -10,000 ≤ C ≤ 10,000)$가 주어진다. 

출력

만약 1번 도시에서 출발해 어떤 도시로 가는 과정에서 시간을 무한히 오래 전으로 되돌릴 수 있다면 첫째 줄에 -1을 출력한다. 그렇지 않다면 N-1개 줄에 걸쳐 각 줄에 1번 도시에서 출발해 2번 도시, 3번 도시, ..., N번 도시로 가는 가장 빠른 시간을 순서대로 출력한다. 만약 해당 도시로 가는 경로가 없다면 대신 -1을 출력한다.

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

 

벨만 포드 (Bellman Ford) 알고리즘

모든 도시에 대해서 초기 소요시간을 큰 수로 설정하고, 1번 도시에 대해서 소요시간을 0으로 설정합니다.

모든 도시와 연결된 도시에 대한 탐색을 거치면서 소요시간이 최솟값이 되도록 업데이트를 합니다.

1번 도시를 제외하고 나머지는 초기 소요시간은 큰 수로, 1번과 직/간접으로 연결되지 않으면 업데이트 하지 않습니다.

$($ if dist[a] == INF: continue $)$

 

위 과정을 도시의 수만큼 반복합니다.

마지막 탐색 중에 업데이트가 발생하면 음의 무한대로 발산하는 경우가 있다는 의미 입니다.

더보기

1번 도시와 연결될 수 있는 도시는 최대 n-1번 내에는 연결이 됩니다.

n번째에도 업데이트가 일어나는 것은 소요시간이 계속해서 줄어들 수 있는 상황을 의미합니다.

 

문제가 요구한 조건에 맞게 출력합니다.

import sys
input = sys.stdin.readline
INF = float("inf")

def main():
    N, M = map(int, input().split())
    bus = [dict() for _ in range(N+1)]
    dist = [INF]*(N+1)
    flag = True

    for i in range(M):
        a, b, c = map(int, input().split())
        if c < bus[a].get(b, INF):
            bus[a][b] = c

    dist[1] = 0  # 1번 도시 소요시간 = 0
    for i in range(N):
        for a in range(1, N+1):
            if dist[a] == INF:  # 1번 도시와 연결 x -> 업데이트 x
                continue
            for b, c in bus[a].items():
                # 최솟값으로 업데이트
                if dist[b] <= dist[a] + c:
                    continue
                dist[b] = dist[a] + c
                
                # 마지막 탐색에서 업데이트가 일어나는지 확인
                if i == N - 1:
                    flag = False
                    break
                    
    # 출력
    if flag:  # 마지막 탐색에서 업데이트 x
        for cost in map(str, dist[2:]):
            if cost == 'inf':  # 연결 x -> -1
                print("-1")
            else: print(cost)  # 소요 시간
            
    else:  # 마지막 탐색에 업데이트가 존재 -> 무한히 시간을 뒤로 돌릴 수 있음
        print(-1)

if __name__ == "__main__":
    main()

 

더보기

예제 입력 1 

3 4
1 2 4
1 3 3
2 3 -1
3 1 -2

예제 출력 1 

4
3

예제 입력 2 

3 4
1 2 4
1 3 3
2 3 -4
3 1 -2

예제 출력 2 

-1

예제 입력 3 

3 2
1 2 4
1 2 3

예제 출력 3 

3
-1