티스토리 뷰

알고리즘

플로이드 워셜

잔잔한 물결처럼 2025. 2. 10. 13:08

플로이드 워셜 알고리즘은 그래프에서 최단 거리를 구하는 알고리즘이다. 

기능 장점 단점
모든 노드 간에 최단 경로를 탐색 음수 가중치 에지가 있어도 수행할 수 있음 속도가 느림 O(n3)

 

플로이드 워셜 알고리즘을 사용할 때는 시간 복잡도를 반드시 고려해야 한다.

 

플로이드 워셜 알고리즘의 가장 핵심적인 원리는 A 노드 에서 B 노드를 거쳐 C노드로 갈때  ( A -> B -> C)

A 노드와 C노드의 경로가 최단 경로일때 (A -> B -> C) 중간 경로들인 A노드 에서 B (A -> B -> C)노드 까지 가는 경로도 최단 경로인 것이다.

 

D[A][C] = Math.min(D[A][C], D[A][B] + D[B][C])

A 에서 C까지의 최단 거리는

A 에서 C까지의 기존 거리, A에서 B까지의 거리 + B 에서 C 까지의 거리 중 더 작은 값이다.

 

# 문제

11404번 : 플로이드

https://www.acmicpc.net/problem/11404

이름도 그냥 플로이드인 문제이다.

 

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

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        // 입력: 도시와 버스의 개수
        int cityCount = Integer.parseInt(br.readLine());
        int busCount = Integer.parseInt(br.readLine());

        // 거리 배열 초기화
        int[][] distance = new int[cityCount + 1][cityCount + 1];
        final int INF = Integer.MAX_VALUE / 2; // 무한대 값 (오버플로우 방지)

        for (int i = 1; i <= cityCount; i++) {
            for (int j = 1; j <= cityCount; j++) {
                if (i == j) distance[i][j] = 0; // 자기 자신으로 가는 경로는 0
                else distance[i][j] = INF;     // 초기값은 무한대
            }
        }

        // 입력: 버스 정보 처리
        for (int i = 0; i < busCount; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int startCity = Integer.parseInt(st.nextToken());
            int endCity = Integer.parseInt(st.nextToken());
            int cost = Integer.parseInt(st.nextToken());

            // 최소 비용만 저장
            distance[startCity][endCity] = Math.min(distance[startCity][endCity], cost);
        }

        // 플로이드-워셜 알고리즘: 모든 경로에 대한 최소 비용 계산
        for (int via = 1; via <= cityCount; via++) {       // 경유 도시
            for (int from = 1; from <= cityCount; from++) { // 출발 도시
                for (int to = 1; to <= cityCount; to++) {   // 도착 도시
                    if (distance[from][to] > distance[from][via] + distance[via][to]) {
                        distance[from][to] = distance[from][via] + distance[via][to];
                    }
                }
            }
        }

        // 결과 출력: 연결되지 않은 경우 0 출력
        for (int i = 1; i <= cityCount; i++) {
            for (int j = 1; j <= cityCount; j++) {
                if (distance[i][j] == INF) sb.append(0).append(" ");
                else sb.append(distance[i][j]).append(" ");
            }
            sb.append("\n");
        }

        System.out.println(sb);
    }
}

3중 for 문을 돌릴때 경유도시가 밖에 있어야 하는것이 중요하다

 

# 문제

11780번 : 플로이드2

https://www.acmicpc.net/problem/11780

플로이드 워셜 알고리즘에 경로까지 구하는 문제이다.

위 문제와 똑같은 방식으로 구했지만 경로를 List 에 저장하고 만약에 더 짧은 경로를 발견하면
경로를 담은 list 를 갱신하는 방식으로 구현했다. (A -> C 경로는 삭제 하고 A -> C 경로A -> B 경로와  B -> C 까지 경로 추가)

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 {
    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        // 입력: 도시와 버스의 개수
        int cityCount = Integer.parseInt(br.readLine());
        int busCount = Integer.parseInt(br.readLine());

        // 거리 배열 초기화
        Distance[][] distances = initializeDistances(cityCount);

        // 입력: 버스 정보 처리
        for (int i = 0; i < busCount; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int startCity = Integer.parseInt(st.nextToken());
            int endCity = Integer.parseInt(st.nextToken());
            int cost = Integer.parseInt(st.nextToken());

            updateDistance(distances, startCity, endCity, cost);
        }

        // 플로이드-워셜 알고리즘 실행
        floydWarshall(distances, cityCount);

        // 결과 출력
        printResults(distances, cityCount, sb);

        System.out.println(sb);
    }

    // 거리 배열 초기화 메서드
    private static Distance[][] initializeDistances(int cityCount) {
        final int INF = Integer.MAX_VALUE / 2; // 무한대 값 (오버플로우 방지)
        Distance[][] distances = new Distance[cityCount + 1][cityCount + 1];

        for (int i = 1; i <= cityCount; i++) {
            for (int j = 1; j <= cityCount; j++) {
                distances[i][j] = new Distance();
                if (i == j) distances[i][j].cost = 0; // 자기 자신으로 가는 경로는 0
                else distances[i][j].cost = INF; // 초기값은 무한대
            }
        }
        return distances;
    }

    // 거리 업데이트 메서드
    private static void updateDistance(Distance[][] distances, int startCity, int endCity, int cost) {
        if (distances[startCity][endCity].cost > cost) {
            distances[startCity][endCity].cost = cost;
            distances[startCity][endCity].list.clear();
            distances[startCity][endCity].list.add(startCity);
            distances[startCity][endCity].list.add(endCity);
        }
    }

    // 플로이드-워셜 알고리즘 메서드
    private static void floydWarshall(Distance[][] distances, int cityCount) {
        for (int via = 1; via <= cityCount; via++) {       // 경유 도시
            for (int from = 1; from <= cityCount; from++) { // 출발 도시
                for (int to = 1; to <= cityCount; to++) {   // 도착 도시
                    if (distances[from][to].cost > distances[from][via].cost + distances[via][to].cost) {
                        distances[from][to].cost = distances[from][via].cost + distances[via][to].cost;

                        // 경로 갱신
                        List<Integer> newPath = new ArrayList<>(distances[from][via].list);
                        newPath.addAll(distances[via][to].list.subList(1, distances[via][to].list.size()));
                        distances[from][to].list = newPath;
                    }
                }
            }
        }
    }

    // 결과 출력 메서드
    private static void printResults(Distance[][] distances, int cityCount, StringBuilder sb) {
        final int INF = Integer.MAX_VALUE / 2;

        // 최소 비용 출력
        for (int i = 1; i <= cityCount; i++) {
            for (int j = 1; j <= cityCount; j++) {
                if (distances[i][j].cost == INF) sb.append(0).append(" ");
                else sb.append(distances[i][j].cost).append(" ");
            }
            sb.append("\n");
        }

        // 경로 출력
        for (int i = 1; i <= cityCount; i++) {
            for (int j = 1; j <= cityCount; j++) {
                if (distances[i][j].cost == INF) {
                    sb.append(0).append("\n");
                } else {
                    sb.append(distances[i][j].list.size()).append(" ");
                    for (int k : distances[i][j].list) {
                        sb.append(k).append(" ");
                    }
                    sb.append("\n");
                }
            }
        }
    }
}

// 거리와 경로를 저장하는 클래스
class Distance {
    int cost;
    List<Integer> list;

    public Distance() {
        this.cost = Integer.MAX_VALUE / 2; // 초기값은 무한대
        this.list = new ArrayList<>();
    }
}

'알고리즘' 카테고리의 다른 글

세그먼트 트리 합구하기  (0) 2025.02.14
세그먼트 트리  (0) 2025.02.13
숨바꼭질 3  (0) 2025.02.13
노드사이의 거리  (0) 2025.02.11
트리 순회하기  (0) 2025.02.09
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함