최단거리 구하기 + 음수 사이클 판별 문제
https://www.acmicpc.net/problem/11657
📌 문제 요약
- N개의 도시와 M개의 버스(간선)가 주어짐
 
- 1번 도시에서 출발해 나머지 모든 도시까지 최단거리 구하기
 
- 조건
- 음수 사이클 존재 시 
-1 출력 
- 도달 불가능한 도시는 
-1 출력 
 
🧠 접근 과정
1️⃣ 첫 번째 접근 — 다익스트라 알고리즘
- 아이디어 : 다익스트라 + 이전 노드를 기억해서 사이클 체크 시도
 
- 문제 발생 ⚠️
- 음수 사이클은 꼭 다시 되돌아가는 경로가 아니어도 발생 가능
 
- 다른 경로로 인해 최단거리가 무한히 갱신될 수 있음
 
- 다익스트라는 음수 가중치/사이클 문제에 부적절
 
 
2️⃣ 두 번째 접근 — 다익스트라 + 유니온파인드
- 아이디어 : 유니온파인드로 사이클 여부 체크
 
- 문제 발생 ⚠️
- 최단거리가 다른 경로로 갱신되는 상황을 보장하지 못함
 
- 다익스트라 한계 그대로 노출
 
 
3️⃣ 세 번째 접근 — 벨만포드 알고리즘 (정답)
- 아이디어 : DP 개념 기반으로 모든 경로 탐색
 
- 핵심 동작
- N-1번 전체 간선 탐색 → 최단거리 갱신
 
- 이후 1번 더 탐색 시 값 갱신 → 음수 사이클 존재
 
 
💡 벨만포드 알고리즘 핵심 로직
거리 초기화
for (int i = 0; i < N + 1; i++) {
    dp[i] = Long.MAX_VALUE;
}
dp[1] = 0;
최단거리 갱신 (N-1번 반복)
for (int i = 0; i < N - 1; i++) {
    for (int j = 0; j < connInfo.size(); j++) {
        int from = connInfo.get(j)[0];
        int to = connInfo.get(j)[1];
        int val = connInfo.get(j)[2];
        if (dp[from] == Long.MAX_VALUE) continue;
        dp[to] = Math.min(dp[to], dp[from] + val);
    }
}
음수 사이클 체크
for (int i = 0; i < connInfo.size(); i++) {
    int from = connInfo.get(i)[0];
    int to = connInfo.get(i)[1];
    int val = connInfo.get(i)[2];
    if (dp[from] == Long.MAX_VALUE) continue;
    if (dp[to] > dp[from] + val) {
        sb.append(-1);
        return;
    }
}
정답 출력 처리
for (int i = 2; i < N + 1; i++) {
    sb.append((dp[i] == Long.MAX_VALUE) ? -1 : dp[i]).append("\n");
}
🐛 반례 / 주의할 점
문제 상황
| 항목 | 값 | 
|---|
| 최대 N | 500 | 
| 최대 M | 6000 | 
| 최소 가중치 | -1000 | 
- 최단거리 값이 
500 * 6000 * -1000 정도까지 갈 수 있음   
Integer 범위 초과 위험 🚨 
해결 방법 💡
- 최단거리 배열 
dp[]를 long 타입으로 선언하여 해결 
✍️ 회고 / 느낀점
| 접근 | 결과 | 비고 | 
|---|
| 다익스트라 | 실패 | 음수 가중치/사이클 처리 불가 | 
| 다익스트라 + 유니온파인드 | 실패 | 최단거리 갱신 보장 X | 
| 벨만포드 | 성공 | 음수 간선/사이클 처리 최적화 | 
느낀 점
- 음수 간선, 사이클 문제 → 벨만포드 필수 고려  
 
- 자료형(
int vs long) 범위 체크는 습관화   
- 문제 조건/범위 꼼꼼히 확인하기  
 
- 사이클 노드 추적은 DFS/BFS 활용 가능  
 
전체 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
    private static StringTokenizer st;
    private static StringBuilder sb = new StringBuilder();
    private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    private static int N, M;
    private static List<int[]> connInfo = new ArrayList<>();
    private static long[] dp;
    public static void main(String[] args) throws IOException {
        inputSetting();
        solution();
        System.out.println(sb);
    }
    private static void solution() {
        
        
        for (int i = 0; i < N + 1; i++) {
            dp[i] = Long.MAX_VALUE;
        }
        dp[1] = 0;
        for (int i = 0; i < N - 1; i++) {
            for (int j = 0; j < connInfo.size(); j++) {
                int from = connInfo.get(j)[0];
                int to = connInfo.get(j)[1];
                int val = connInfo.get(j)[2];
                if (dp[from] == Long.MAX_VALUE) continue;
                dp[to] = Math.min(dp[to], dp[from] + val);
            }
        }
        
        for (int i = 0; i < connInfo.size(); i++) {
            int from = connInfo.get(i)[0];
            int to = connInfo.get(i)[1];
            int val = connInfo.get(i)[2];
            if (dp[from] == Long.MAX_VALUE) continue;
            if (dp[to] > dp[from] + val) {
                sb.append(-1);
                return;
            }
        }
        for (int i = 2; i < N + 1; i++) {
            sb.append((dp[i] == Long.MAX_VALUE) ? -1 : dp[i]).append("\n");
        }
        
    }
    private static void inputSetting() throws IOException {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        dp = new long[N + 1];
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int from = Integer.parseInt(st.nextToken());
            int to = Integer.parseInt(st.nextToken());
            int val = Integer.parseInt(st.nextToken());
            connInfo.add(new int[]{from, to, val});
        }
    }
}
🔗 참고 링크