[알고리즘] 벨만포드 알고리즘

1. Bellman-Ford 알고리즘이란 무엇입니까?

  • 노드에서 다른 모든 노드까지의 최단 거리를 찾는 알고리즘 + 음의 주기가 있는지 알기
  • 다익스트라와 달리 음수 가중치와 함께 사용할 수도 있습니다.
  • 시간 복잡도: O(NE) (N: 노드 수, E: 에지 수)


  • 다음 다이어그램이 있다고 가정합니다.
  • Dijkstra를 사용하여 노드 1에서 모든 노드까지의 거리 찾기
  • 1 -> 2 = 1
  • 1 -> 2 -> 3 = 2
  • 1 -> 2 -> 3 -> 4 = 4
  • 노드 2는 이미 방문한 적이 있으므로 경로 4 -> 2는 고려하지 않습니다.
  • 따라서 1 -> 2의 최단 경로는 1입니다.
  • 그러나 1 -> 2에서 실제 최단 경로는 -∞입니다.
  • Bellman-Ford 알고리즘은 이 문제를 해결하기 위해 개발되었습니다.

2. 알고리즘 동작 원리

  • 시작점을 선택하십시오.
  • 모든 에지를 검색하므로 시작 노드에서 다른 노드까지의 거리가 INF가 아닌 경우 거리가 업데이트됩니다. 이 과정 (정점 수 -1)가능한 한 여러 번 계속
  • 마지막 두 개를 수행하십시오.

2단계에서 (꼭지점 수 – 1)까지 계속하는 이유는 V 꼭지점과 E 모서리를 갖는 가중치 그래프에서 꼭지점 A에서 꼭지점 B까지의 최단 거리가 최대 V-1강아지의 끝을 통과하기 때문입니다.

V-1개 이상의 정점을 방문하면 결국 중복 방문이 발생하므로 최단 경로를 결정할 수 없습니다.

또한 3단계가 끝나면 2단계를 다시 수행하여 음의 주기가 있는지 판단한다. V 꼭지점을 통과한 후 최단 경로를 업데이트하면 비용이 무한대로 업데이트되기 때문에 음의 순환이 발생하고 최단 경로를 얻을 수 없습니다.

사진과 함께 알아봅시다.


1. 재설정

시작점(시작 노드)을 제외한 모든 노드까지의 최단 거리를 무한대로 초기화합니다. 시작점으로부터의 거리는 0입니다.


2. 최단거리 계산

V-1 동안 모든 모서리가 검사됩니다.

현재 에지까지의 최단 거리 + 현재 에지까지의 거리가 현재 에지의 target에 저장된 최단 거리보다 짧으면 최단 거리를 업데이트합니다.

for(i=1; i<V; i++) // V-1번
	for(Edge now : EdgeList) // 모든 엣지에 대해
		if(distance(now.end) > dist(now.start) + now.cost)
			distance(now.end) = dist(now.start) + now.cost




이 시점에서 에지의 시작점이 무한이면 업데이트가 되지 않으므로 첫 번째 검색에서는 시작 꼭지점에 연결된 에지만 업데이트 됩니다.

즉, 첫 번째 탐색에서 최대 하나의 모서리를 포함하는 최단 거리를 구합니다.

두 번째는 여전히 한 방향으로 갈 수 있으므로 최대 두 개의 가장자리를 포함하는 최단 거리를 얻습니다.

따라서 i번째 탐색에서는 i번째 edge를 포함하는 최단 거리를 구하게 되며, 경로에 어떠한 싸이클도 발생하지 않아야 하므로 최단 거리 탐색은 V-1로 끝난다.

3. V. 검색 – 확인 프로세스

최단 거리 탐색은 V-1에서 끝나므로 탐색을 계속해도 업데이트가 발생하지 않아야 합니다.

업데이트는 Vth에서 발생 → 음의 주기가 존재합니다.

for(Edge now : EdgeList)
	if(distance(now.end) > dist(now.start) + now.cost)
		return false // 음수 사이클 존재
return true

예제 문제 해결

https://www.acmicpc.net/problem/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을 인쇄하십시오. 그렇지 않으면 1번 도시에서 2번 도시, 3번 도시, …, N번 도시로 가는 가장 빠른 시간이 N-1행에 걸쳐 각 행에 순차적으로 인쇄된다. 해당 도시로 가는 경로가 없으면 대신 -1을 반환합니다.

 public class BOJ11657 {

	static class Edge {
		int start, end, cost;

		public Edge(int start, int end, int cost) {
			this.start = start;
			this.end = end;
			this.cost = cost;
		}
	}

	static int N, M;
	static long() dist;
	static Edge() edgeList;
	static int ans;
	static boolean isUpdated;

	static final int INF = 100000000;

	public static void main(String() args) throws Exception {
		/* INPUT */
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		StringTokenizer st;
		st = new StringTokenizer(br.readLine());
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());

		edgeList = new Edge(M + 1);

		int a, b, c;
		for (int i = 1; i <= M; i++) {
			st = new StringTokenizer(br.readLine());
			a = Integer.parseInt(st.nextToken());
			b = Integer.parseInt(st.nextToken());
			c = Integer.parseInt(st.nextToken());

			edgeList(i) = new Edge(a, b, c);
		}

		/* 1. 배열 초기화 */
		dist = new long (N+1);
		for (int i = 2; i <= N; i++) {
			dist(i) = INF;
		}

		BellmanFord();

		/* OUTPUT */
		if (isUpdated) {                 // 음수 사이클이 발생한 경우
			System.out.print(-1);
		}
		else {
			StringBuilder sb = new StringBuilder();
			for (int i = 2; i<=N; i++) {
				if (dist(i)==INF) {        // 가는 경로가 없는 경우
					sb.append("-1\n");
				}
				else {
					sb.append(dist(i)+"\n");
				}
			}
			System.out.print(sb);
		}
	}

	static void BellmanFord() {
		/* 2. 최단거리 계산 */
		for (int i = 1; i < N; i++) {           // N-1번 탐색
			for (Edge now : edgeList) {           // 모든 엣지에 대해
				if (dist(now.start) == INF)         // 시작점이 무한대면 어차피 갱신이 안됨
					continue;
				dist(now.end) = Math.min(dist(now.end), dist(now.start) + now.cost); // 갱신
			}
		}

		/* 3. 사이클 발생 여부 확인 */
		for (Edge now : edgeList) {
			if (dist(now.start) + now.cost < dist(now.end)) {
				isUpdated = true;
				return;
			}
		}
	}
}

참조 페이지

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