
벨만-포드(Bellman-Ford)는 음수 가중치가 포함된 그래프에서 최단 경로를 찾는 알고리즘이다.
| 구분 | 다익스트라 (Dijkstra) | 벨만-포드 (Bellman-Ford) |
|---|---|---|
| 음수 가중치 | 불가능 | 가능 |
| 음수 사이클 감지 | 불가능 | 가능 |
| 시간 복잡도 | O(E log V) | O(V × E) |
| 구현 특징 | 우선순위 큐 사용 | 모든 간선을 V-1번 반복 |
최단 경로는 최대 V-1개의 간선을 포함한다.
(사이클이 없는 한, 더 이상 추가 간선을 거치지 않아도 모든 노드에 도달 가능)
| 반복 횟수 | 의미 |
|---|---|
| 0 | 시작점 |
| 1 | 시작점 → 1개 간선 |
| 2 | 시작점 → 2개 간선 |
| ... | ... |
| V-1 | 시작점 → V-1개 간선 (모든 노드 도달 가능) |
V번째 반복에서도 거리가 갱신된다면, 음수 사이클이 존재한다는 의미다.
// V-1번 완화
for (int i = 1; i < V; i++) {
for (모든 간선 u → v) {
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
// V번째 반복 - 음수 사이클 감지
for (모든 간선 u → v) {
if (dist[u] + weight < dist[v]) {
return "음수 사이클 존재!";
}
}
핵심 아이디어
가상 노드 추가
for (int i = 1; i <= inputArr[0]; i++) {
graph.computeIfAbsent(0, k -> new ArrayList<>())
.add(new int[]{i, 0}); // 0 → 모든 노드, 가중치 0
}
이유
핵심 아이디어
양의 사이클 감지 및 전파
boolean[] inPositiveCycle = new boolean[n + 1];
// 1단계: 양의 사이클 존재 확인
for (모든 간선 u → v) {
if (balance[u] + w > balance[v]) {
inPositiveCycle[v] = true;
}
}
// 2단계: 사이클 영향 전파 (V-1번 반복)
for (int i = 1; i < n; i++) {
for (모든 간선 u → v) {
if (inPositiveCycle[u]) {
inPositiveCycle[v] = true;
}
}
}
왜 전파가 필요한가?
핵심 아이디어
복합 갱신 조건
if(balance[u] != Long.MIN_VALUE &&
balance[u] + w + earn[v] > balance[v]) {
balance[v] = balance[u] + w + earn[v];
}
BFS로 사이클 영향 전파
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < N; i++) {
if (inPositiveCycle[i]) queue.offer(i);
}
while (!queue.isEmpty()) {
int u = queue.poll();
if(graph.containsKey(u)) {
for(var edge : graph.get(u)) {
int v = edge[0];
if (!inPositiveCycle[v]) {
inPositiveCycle[v] = true;
queue.offer(v);
}
}
}
}
BFS 방식은 불필요한 반복 완화를 줄여 효율적이다.
// 1. 초기화
int[] dist = new int[N];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
// 2. V-1번 완화
for (int i = 1; i < N; i++) {
for (모든 간선 u → v, weight w) {
if (dist[u] != Integer.MAX_VALUE &&
dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
// 3. 음수 사이클 감지
for (모든 간선 u → v, weight w) {
if (dist[u] != Integer.MAX_VALUE &&
dist[u] + w < dist[v]) {
// 음수 사이클 존재!
}
}
| 문제 유형 | 변형 방식 |
|---|---|
| 최장 경로 | 가중치 부호 반전, 비교 부등호 반대 |
| 시작점 불명 | 가상 노드 추가 |
| 사이클 영향 전파 | BFS/DFS 또는 V-1번 추가 완화 |
| 복합 계산 | 갱신 시 수입/비용 포함 |
// 잘못된 방식
if (dist[u] + w < dist[v])
// 올바른 방식
if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v])
총합: O(V × E)
| 사용해야 하는 경우 | 사용하지 말아야 하는 경우 |
|---|---|
| 음수 가중치 존재 | 모든 가중치가 양수 |
| 음수/양수 사이클 감지 필요 | 단순 최단 경로 탐색 |
| 시작점이 불명확 | 그래프가 매우 큼 (V × E 과다) |
세 문제는 모두 벨만-포드의 핵심인 V-1번 완화 + V번째 사이클 감지 원리를 기반으로 한다.
하지만 각 문제는 그래프 구조나 요구 조건에 맞게 다음과 같이 변형되었다.
| 문제 | 주요 포인트 |
|---|---|
| 1865 웜홀 | 음수 사이클 감지, 가상 노드 활용 |
| 1738 골목길 | 최장 경로 + 양의 사이클 전파 |
| 1219 오민식의 고민 | 수입/비용 계산 + BFS 전파 |
결국 벨만-포드는 “경로 완화 기반의 상태 갱신 알고리즘”이다.
그래프가 복잡해질수록, 이 기본 구조를 문제에 맞게 변형하는 능력이 중요하다.