본문 바로가기
Algorithm/Graph Search Algorithm

Floyd-Warshall, 플로이드 워샬 알고리즘

by rueMi 2024. 2. 17.

그래프 문제에서 최단 경로를 구하는 알고리즘은 다음과 같다.

Dijkstra 다익스트라 한 지점에서 다른 모든 지점까지 최단 거리 양의 가중치만 O(ElogV)
Bellman-ford 밸만-포드 한 지점에서 다른 모든 지점까지 최단 거리 음의 가중치 허용 O(VE)
Floyd-Warshall 플로이드 워샬 모든 지점에서 다른 모든 지점까지 최단 거리 음의 가중치 허용 O(V^3)

 

다익스트라와 밸만 포드에 대해서는 다음 포스팅에서 알아볼 수 있다.

 

다익스트라, Dijkstra Algorithm

코딩 문제에서 많이 볼 수 있는 그래프와 관련된 알고리즘을 공부해보자. 그래프에서 최단 거리를 구하는 알고리즘은 다음과 같은 것들이 있다. 💡 그래프의 최단 거리 구하는 알고리즘 1. 다익

rue-mi.tistory.com

 

Bellman-Ford 알고리즘

흔히 그래프에서 최단 거리를 구하는 알고리즘은 다익스트라를 떠올리기 쉽다. 하지만 다익스트라는 음수 가중치가 있는 경우에는 사용이 불가능하다. 다익스트라에 대한 자세한 설명과 과정

rue-mi.tistory.com

 


Floyd-Warshall, 플로이드 워샬

❓ Floyd-Warshall, 플로이드 워샬

그래프에서 모든 정점 사이의 최단 거리를 구하는 알고리즘이다. 음의 사이클이 없는 한 음의 가중치가 있어도 잘 실행되는 최단 거리 알고리즘이다.

 

기본 컨셉

 

기본 컨셉은 하나이다. DP 개념을 활용한다.

정점 i, j 사이 모든 경유지를 탐색해서 그 중 최단 경로를 찾는다. 그래프의 정점이 1~V까지이면, 경유지 K는 이 모든 정점을 경유지로 하는 경로를 탐색한다.

DP[i][j] = Math.min(DP[i][j], DP[i][k] + DP[k][j]

 

알고리즘 동작 과정

  1. 2차원 배열을 만들고 그래프의 간선의 정보를 저장한다.
    1. 두 정점이 직접적으로 연결되어 있지 않으면 INF값, 자기 자신으로의 거리는 0으로 설정한다.
  2. 경유지를 1~V까지 순회한다
    1. 간선 정보를 저장한 2차원 배열을 순회하며 (i~j)까지의 거리와 (i~k + k~j)까지의 거리 중 짧은 값으로 업데이트한다.

 

예시

이 그래프를 토대로 각 정점간의 거리를 저장한 2차원 배열을 만들어보자.

  1 2 3 4
1 0 4 -1 8
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

 

 

경유지를 1로 설정했을 때는 다음과 같다.

  1 2 3 4
1 0 4 -1 8
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

 

1을 거쳐서 가는 경로가 없기 때문에, 즉 1열이 모두 INF 값이기 때문에 아무것도 변화하지 않는다.

 

 

경유지를 2로 설정했을 때는 다음과 같다.

  1 2 3 4
1 0 4 -1 8 ⇒ -6
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

 

1은 2를 거쳐서 다른 노드로 갈 수 있기 때문에, 1 → 4보다, 1 → 2 → 4가 더 짧아서 -6으로 업데이트 되었다.

 

 

경유지를 3으로 설정했을 때는 다음과 같다.

  1 2 3 4
1 0 4 -1 -6
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

 

1 → 3, 2 → 3으로 갈 수 있다.

  • 1→ 3 → 다른 노드
    • 1 → 3 → 4만 가능하다.
      • 하지만 1 → 3 → 4는 2이고, 1 → 4는 -6이기 때문에 업데이트 되지 않는다.
  • 2 → 3 → 다른 노드
    • 2 → 3 → 4만 가능하다
      • 하지만 2 → 3 → 4는 1이고, 2 → 4는 -10이기 때문에 업데이트 되지 않는다.

따라서 표는 업데이트 되지 않는다.

 

 

경유지를 4로 설정하면 다음과 같다.

  1 2 3 4
1 0 4 -1 -6
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

 

(1 → 4), (2 → 4), (3 → 4) 모두 가능하지만, 4에서 갈 수 있는 노드가 없기 때문에 표는 업데이트 되지 않는다.

 

 

이렇게 최종 배열이 나왔고, 이것이 바로 플로이드 워샬을 적용한, 모든 노드에서 모든 노드로 가는 최단 거리 배열이다.

  1 2 3 4
1 0 4 -1 -6
2 INF 0 -2 -10
3 INF INF 0 3
4 INF INF INF 0

코드

아래 문제를 풀어보면서 코드를 정리해보자.

 

11404번: 플로이드

첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가

www.acmicpc.net

 

자료구조

 

우선 플로이드 알고리즘에 필요한 자료구조는 간선 정보를 저장한 graph 하나뿐이다. 이 그래프를 가지고 업데이트하고 계속 할 것이다.

또한 무한의 값으로 사용할 INF 값도 정의해주자. 아래에서 두 INF 값을 더하는 코드가 있기 때문에 / 2 + 1을 해주었다.

static int[][] graph;
static int INF = Integer.MAX_VALUE / 2 -1;

 

간선 정보를 입력 받기 전, graph를 초기화해주어야 한다. 모든 값을 INF로 채우고, 자기 자신으로 가는 값은 0으로 바꿔준다.

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(in.readLine());
int M = Integer.parseInt(in.readLine());
graph = new int[N+1][N+1];
for (int i = 0; i < N+1; i++){
    Arrays.fill(graph[i], INF);
    graph[i][i] = 0; //자기자신은 0
}

 

간선 정보를 입력 받으며 최소인 값으로 갱신해준다. 문제에서 두 지점 사이의 간선이 여러 개일 수도 있다고 했기 때문에 최소로 갱신해주는 코드로 작성했다.

while(M -- > 0){
    StringTokenizer st = new StringTokenizer(in.readLine());
    int a = Integer.parseInt(st.nextToken());
    int b = Integer.parseInt(st.nextToken());
    int cost = Integer.parseInt(st.nextToken());
    graph[a][b] = Math.min(graph[a][b], cost); //노선이 여러개일 수 있다고 했으니까
}

 

알고리즘을 수행하는 코드이다. 간단하다. 경유지를 돌면서, 모든 간선 정보를 업데이트 해주면 된다. DP와 비슷하다.

//floyd-warshall
//경유지
for (int k = 1; k <= N; k++){
    for (int i = 0; i <= N; i++){
        for (int j = 0; j <= N; j++){
            graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
        }
    }
}

 

그리고 이를 출력하는 코드이다.

StringBuilder sb = new StringBuilder();
for (int i = 1; i <= N; i++){
    for (int j = 1; j <= N; j++){
       sb.append((graph[i][j] == INF)? 0 : graph[i][j]).append(" ");
   }
    sb.append("\\n");
}
System.out.println(sb);

 

전체 코드

package ps.ㄱSolving;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class BOJ_G4_11404_플로이드 {
    static int[][] graph;
    static int INF = Integer.MAX_VALUE / 2 -1;
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(in.readLine());
        int M = Integer.parseInt(in.readLine());
        graph = new int[N+1][N+1];
        for (int i = 0; i < N+1; i++){
            Arrays.fill(graph[i], INF);
            graph[i][i] = 0; //자기자신은 0
        }
        while(M -- > 0){
            StringTokenizer st = new StringTokenizer(in.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            int cost = Integer.parseInt(st.nextToken());
            graph[a][b] = Math.min(graph[a][b], cost); //노선이 여러개일 수 있다고 했으니까
        }

        //floyd-warshall
        //경유지
        for (int k = 1; k <= N; k++){
            for (int i = 0; i <= N; i++){
                for (int j = 0; j <= N; j++){
                    graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
                }
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= N; i++){
            for (int j = 1; j <= N; j++){
                sb.append((graph[i][j] == INF)? 0 : graph[i][j]).append(" ");
            }
            sb.append("\\n");
        }
        System.out.println(sb);
    }
}

참고자료

 

[Algorithm]Floyd-Warshall(플로이드-워샬 알고리즘)

Floyd-Warshall(플로이드-워샬 알고리즘)은 그래프에서 모든 정점 사이의 최단거리를 구하는 알고리즘입니다. 시간 복잡도는 O(V^3)을 가지죠. 이 알고리즘은 음의 사이클이 없는 그래프에 대해서 모

engkimbs.tistory.com

 

 

알고리즘 - 최단 경로 알고리즘 (다익스트라 알고리즘, 벨만-포드 알고리즘, 플로이드-워셜 알고

알고리즘 - 최단 경로 알고리즘 (다익스트라 알고리즘, 벨만-포드 알고리즘, 플로이드-워셜 알고리즘) 1. 최단 경로 알고리즘 최단 경로 문제란 가중 그래프에서 간선의 가중치의 합이 최소가 되

jina-developer.tistory.com

 

'Algorithm > Graph Search Algorithm' 카테고리의 다른 글

BOJ G3 2151 거울 설치 JAVA  (0) 2024.02.19
BOJ G4 11404 플로이드 JAVA  (0) 2024.02.17
BOJ G2 2931 가스관 JAVA  (0) 2024.02.16
BOJ G3 1865 웜홀 JAVA  (0) 2024.02.10
Bellman-Ford 알고리즘  (0) 2024.02.09