[Algorithm] 벨만-포드(Bellman-Ford) 알고리즘

PikminProtectionAssociation·2024년 6월 24일

행성 탈출기

목록 보기
5/21
post-thumbnail

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

정의 및 특징

  • 한 노드에서 다른 노드까지의 최단 거리를 구하는 알고리즘
  • 간선의 가중치가 음수일 때도 최단 거리를 구할 수 있음
  • Dijkstra 알고리즘의 한계를 해결한 알고리즘
    • 매 단계마다 모든 간선을 전부 확인하면서 모든 노드 간의 최단 거리를 구함
    • 다익스트라 알고리즘은 방문하지 않은 노드 중에서 최단 거리가 가장 가까운 노드만 방문
  • 음수 간선의 순환을 감지할 수 있음
    • v번째 탐색을 수행한 뒤 업데이트 되는 값의 존재 여부 발견 = 음수 사이클 존재
    • 음수 사이클이 존재하면 비용을 무한히 줄일 수 있게 되어 최단 거리를 구할 수 없음
    • V-1 단계까지 진행하면 모든 노드에 대한 최단 거리가 확정되고, 여기서 한번 더 단계를 진행했을 때 최단 거리 테이블이 갱신 된다면 이는 음수 사이클이 존재하는 것

시간 복잡도

  • O(VE) : V번 반복에 대해 해당 정점과 연결된 모든 간선 E를 탐색

Floyd Warshall vs Bellman-Ford

차이점

  • Floyd Warshall 알고리즘은 모든 쌍에 대한 최단경로를 구할 수 있음
  • Bellman-Ford 알고리즘은 시작점에서의 최단경로만 알 수 있음

공통점

  • 음의 가중치를 갖는 간선이 존재할 수 있음

예제

백준 11657 타임머신

문제

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을 출력한다.


문제

  • 문제에서 요구하는 포인트는 두 가지이다.
    • 출발지가 정해져 있다.
    • 음수 비용을 가질 수 있다.
  • 출발지가 정해져 있다
    → 다익스트라, 벨만-포드
  • 음수 비용을 가질 수 있다.
    → 플로이드 와샬, 벨만-포드

해결

  • 위 두 조건을 생각했을 때 가장 적절한 알고리즘은 벨만-포드 알고리즘이라는 결론을 내릴 수 있다!

코드

static void Main(string[] args)
{
	const int INF = 1_000_000_000;
	string[] str1 = Console.ReadLine().Split(" ");
    int N = int.Parse(str1[0]);
    int M = int.Parse(str1[1]);
    
    List<int[]> edges = new List<int[]>();
    for (int i = 0; i < M; i++)
    {
    	string[] str2 = Console.ReadLine().Split(" ");
        int start = int.Parse(str2[0]) - 1;
        int end = int.Parse(str2[1]) - 1;
        int cost = int.Parse(str2[2]);
        
        edges.Add(new int[] { start, end, cost });
    }
    
    long[] dist = new long[N];
    Array.Fill(dist, INF);
    dist[0] = 0;
    
    bool isCycle = false;
    for (int i = 0; i < N; i++)
    {
    	for (int j = 0; j < edges.Count; j++)
        {
        	int start = edges[j][0];\
            int end = edges[j][1];
            int cost = edges[j][2];
            
            if (dist[start] != INF && dist[end] > dist[start] + cost)
            {
            	dist[end] = dist[start] + cost;
                
                if (i == N - 1) isCycle = true;
            }
        }
    }
    
    if (isCycle)
    {
    	Console.WriteLine(-1);
        return;
    }
    
    for (int i = 1; i < dist.Length; i++)
    {
    	if (dist[i] == INF) Console.WriteLine(-1);
        else Console.WriteLine(dist[i]);
    }
}
  • 모든 단계를 진행한 뒤에 또 최소 비용이 갱신되는지에 따라 사이클 여부를 결정하면 된다!



출처

[알고리즘] 벨만-포드 알고리즘 (Bellman-Ford Algorithm)



끗~

0개의 댓글