🔎 음수 가중치를 허용하는 그래프에서 최단 경로를 구하는 알고리즘.
O(nm) 시간 복잡도를 가진다.시작 노드를 설정한 다음 시작 노드의 최소 비용은 0, 나머지 노드는 INF로 초기화한다. 이후 최소 비용을 갱신할 때 직전 노드도 갱신한다.
노드 개수 – 1만큼 다음 연산을 반복한다.
2-1. 시작 노드에서 갈 수 있는 각 노드에 대하여 전체 노드 각각을 거쳐갈 때 현재까지 구한 최소 비용보다 더 적은 최소 비용이 있는지 확인하여 갱신한다. 최소 비용을 갱신할 때, V의 직전 노드 값도 같이 갱신한다.
과정 2를 마지막으로 한 번 더 수행하여 갱신되는 최소 비용이 있는지 확인. 만약 있다면 음의 순환이 있음을 의미한다.
벨만-포드 알고리즘은 글로만 읽으면 쉽게 와닿지 않습니다. 아마도 ‘노드 개수 – 1만큼 연산을 반복하라’는 말과 과정 3의 ‘한 번 더 수행하라’는 말이 쉽게 와닿지 않을 겁니다. 우선은 벨만-포드 알고리즘이 동작하는 과정을 자세히 살펴보고 그 이후에 왜 그렇게 하는지에 대해 설명해보겠습니다.


⏰
최소비용(A)(0) == 최소비용(A)(0) + 간선(A, A)(0) : 갱신하지 않음
최소비용(B)(INF) > 최소비용(A)(0) + 간선(A, B)(4) : 최소비용(B)를 INF에서 4로 갱신
최소비용(C)(INF) > 최소비용(A)(0) + 간선(A, C)(3) : 최소비용(C)를 INF에서 3으로 갱신
최소비용(D)(INF) == 최소비용(A)(0) + 간선(A, D)(INF) : 갱신하지 않음
최소비용(E)(INF) > 최소비용(A)(0) + 간선(A, E)(-6) : 최소_비용(E)를 -6으로 갱신

⏰
최소비용(A)(0) < 최소비용(B)(4) + 간선(B, A)(INF) : 갱신하지 않음
최소비용(B)(4) == 최소비용(B)(4) + 간선(B, B)(0) : 갱신하지 않음
최소비용(C)(3) < 최소비용(B)(4) + 간선(B, C)(INF) : 갱신하지 않음
최소비용(D)(INF) > 최소비용(B)(4) + 간선(B, D)(5) : 9로 갱신
최소비용(E)(-6) < 최소비용(B)(4) + 간선(B, E)(-6) : 갱신하지 않음

⏰
최소비용(A)(0) < 최소비용(C)(3) + 간선(C, A)(INF) : 갱신하지 않음
최소비용(B)(4) < 최소비용(C)(3) + 간선(C, B)(2) : 갱신하지 않음
최소비용(C)(3) == 최소비용(C)(3) + 간선(C, C)(0) : 갱신하지 않음
최소비용(D)(9) < 최소비용(C)(3) + 간선(C, D)(INF) : 갱신하지 않음
최소비용(E)(-6) < 최소비용(C)(3) + 간선(C, E)(INF) : 갱신하지 않음

⏰
최소비용(A)(0) < 최소비용(E)(-6) + 간선(E, A)(INF) : 갱신하지 않음
최소비용(B)(4) < 최소비용(E)(-6) + 간선(E, B)(2) : 갱신하지 않음
최소비용(C)(3) == 최소비용(E)(-6) + 간선(E, C)(0) : -4로 갱신
최소비용(D)(9) < 최소비용(E)(-6) + 간선(E, D)(INF) : 갱신하지 않음
최소비용(E)(-6) < 최소비용(E)(-6) + 간선(E, E)(INF) : 갱신하지 않음

⏰
+ : 노드 A에서 A를 거쳐 각 노드까지 가는 비용을 갱신
최소비용(A)(0) == 최소비용(A)(0) + 간선(A, A)(0) : 갱신하지 않음
최소비용(B)(4) == 최소비용(A)(0) + 간선(A, B)(4) : 최소비용(B)를 INF에서 4로 갱신
최소비용(C)(-4) < 최소비용(A)(0) + 간선(A, C)(3) : 갱신하지 않음
최소비용(D)(9) > 최소비용(A)(0) + 간선(A, D)(INF) : 갱신하지 않음
최소비용(E)(-6) == 최소비용(A)(0) + 간선(A, E)(-6) : 최소비용(E)를 -6으로 갱신
❗ 매 연산마다 최단 경로가 1개씩 확정되므로!
❗ 음수 사이클로 인해 최단 거리가 무한히 줄어드는지 확인하기 위해서
추가로 한 번 더 반복하여 모든 간선을 검사하면, distance[u] + w < distance[v] 조건을 만족하는 간선이 발견될 경우, 이는 음수 가중치 사이클이 존재한다는 것을 의미한다. 따라서, 한 번 더 반복은 음수 가중치 사이클 검출에 필수적이다.

⭐ 정리
그래프에 음의 순환이 있으면 어떤 알고리즘도 최단 경로를 구할 수 없다 ! 벨만 포드 알고리즘은 음의 가중치가 있는 그래프에서 최단 경로를 찾을 수 있는 대신, 음의 순환에 빠질 수 있고, 다익스트라 알고리즘은 음의 가중치가 있는 그래프에서 동작하지 못하므로 아예 언급이 안된다.


위와 같은 그래프가 있을 때 1번 노드에서 다른 모든 노드로 가는 최단 경로를 구하는 문제를 생각해보자.

벨만 - 포드 알고리즘은 간선을 중심으로 동작(모든 간선을 확인)하므로 그래프를 에지 리스트로 구현한다.
최단 경로 리스트
- 출발 노드는 0, 나머지 노드는 무한대로 초기화한다.
- 출발 노드를 1로 선택해 벨만 - 포드 알고리즘을 진행
벨만 포드 알고리즘은 매 반복마다 모드 간선을 확인한다.
모든 에지의 (출발, 종료, 가중치)에서 다음 조건을 만족하면 업데이트를 실행한다.
- 출발 노드가 무한대가 아니고, `종료 노드값 < 출발 노드 값 + 에지 가중치` 이면 `종료 노드 값 = 출발 노드값 + 에지 가중치`로 업데이트
- 즉, D[s] != INF and D[e] > D[s] + w 일 때 D[e] = D[s] + w





📌 STEP 2을 N-1번 (N : 노드 개수) 수행한다.


n(노드 개수), m(에지 개수)
edges(에지 정보 저장 리스트)
distance(거리 리스트) # 무한으로 초기화
for 에지 개수만큼 반복
(s, e, w) # 에지 리스트에 에지 정보 저장
# 벨만 포드 수행
거리 리스트에 출발 노드 0으로 초기화
for 에지 개수 만큼 반복
현재 에지 데이터 가져오기
if 출발 노드가 무한대가 아니고 종료 노드 값 < 출발 노드 값 + 에지 가중치:
업데이트 수행 -> 종료 노드 값 = 출발 노드 값 + 에지 가중치
if n 번째 라운드:
음수 사이클 존재
음수 사이클 존재하면 -> -1 출력
음수 사이클 존재하지 않으면 -> 거리 리스트 출력
import sys
input = sys.stdin.readline
INF = int(1e9) # 무한을 의미하는 값으로 10억을 설정
# 노드의 개수, 간선의 개수를 입력받기
n, m = map(int, input().split())
# 모든 간선에 대한 정보를 담는 리스트 만들기
edges = []
# 최단 거리 테이블을 모두 무한으로 초기화
distance = [INF] * (n + 1)
# 모든 간선 정보를 입력받기
for _ in range(m):
a, b, c = map(int, input().split())
# a번 노드에서 b번 노드로 가는 비용이 c라는 의미
edges.append((a, b, c))
def bf(start):
# 시작 노드에 대해서 초기화
distance[start] = 0
# 전체 n - 1번의 라운드(round)를 반복
for i in range(n):
# 매 반복마다 "모든 간선"을 확인하며
for j in range(m):
cur_node = edges[j][0]
next_node = edges[j][1]
edge_cost = edges[j][2]
# 현재 간선을 거쳐서 다른 노드로 이동하는 거리가 더 짧은 경우
if distance[cur_node] != INF and distance[next_node] > distance[cur_node] + edge_cost:
distance[next_node] = distance[cur_node] + edge_cost
# n번째 라운드에서도 값이 갱신된다면 음수 순환이 존재
if i == n - 1:
return True
return False
# 벨만 포드 알고리즘을 수행
negative_cycle = bf(1) # 1번 노드가 시작 노드
if negative_cycle:
print("-1")
else:
# 1번 노드를 제외한 다른 모든 노드로 가기 위한 최단 거리를 출력
for i in range(2, n + 1):
# 도달할 수 없는 경우, -1을 출력
if distance[i] == INF:
print("-1")
# 도달할 수 있는 경우 거리를 출력
else:
print(distance[i])