개요
문제 이름처럼 플로이드 와샬 알고리즘을 사용한다.
플로이드 와샬은 다익스트라, 벨만포드와 같이 간선의 가중치가 1이 아닌경우에 최단경로를 찾는데 사용된다.
각각의 특징을 짧게 나마 정리하면,
다익스트라 - 한 노드 ▶ 모든 노드 / 빠름 / 가중치가 음수이면 구할 수 없다.
벨만 포드 - 한 노드 ▶ 모든 노드 / 중간 / 가중치가 음수일 때 다익스트라 대신 사용할 수 있다.
플로이드 와샬 - 모든 노드 ▶ 모든 노드 / 느림 / 가중치가 음수여도 사용할 수 있다.
각 알고리즘 문제를 풀어보며 생각되는 가장 간단한 특징들이다.
문제를 보면 모든노드 ▶ 모든 노드로 가는 최단 경로를 구하라는 것을 알 수 있다.
그러므로 플로이드 와샬 알고리즘을 구현하도록 한다.
문제
n(1 ≤ n ≤ 100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1 ≤ m ≤ 100,000)개의 버스가 있다. 각 버스는 한 번 사용할 때 필요한 비용이 있다.
모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.
입력
풀이
INF는 무한대를 의미하며 동시에 경로가 없다는 것을 뜻한다.
INF는 가능한 최대 거리보다 큰 값을 잡아야 한다.
최대 값은 |v - 1| * maxLength 이므로 아래 코드와 같이 INF 값을 설정하였다.
(maxLength는 최대 버스 비용을 뜻한다.)
플로이드 와샬은 아래와 같이 3중 for문으로 구현을 하는데
for(i = 1 -> n)
for(j = 1 -> n)
for(k = 1 -> n)
예를 들어 n = 3인 경우
i, j, k ▶ 1, 1, 1 → 1출발 1경유 1도착
i, j, k ▶ 1, 1, 2 → 1출발 1경유 2도착
i, j, k ▶ 1, 1, 3 → 1출발 1경유 3도착
i, j, k ▶ 1, 2, 1 → 2출발 1경유 1도착
i, j, k ▶ 1, 2, 2 → 2출발 1경유 2도착
i, j, k ▶ 1, 2, 3 → 2출발 1경유 3도착
i, j, k ▶ 1, 3, 1 → 3출발 1경유 1도착
i, j, k ▶ 1, 3, 2 → 3출발 1경유 2도착
i, j, k ▶ 1, 3, 1 → 3출발 1경유 3도착
i, j, k ▶ 2, 1, 1 → 1출발 2경유 1도착
i, j, k ▶ 2, 1, 2 → 1출발 2경유 2도착
i, j, k ▶ 2, 1, 3 → 1출발 2경유 3도착
i, j, k ▶ 2, 2, 1 → 2출발 2경유 1도착
i, j, k ▶ 2, 2, 2 → 2출발 2경유 2도착
i, j, k ▶ 2, 2, 3 → 2출발 2경유 3도착
i, j, k ▶ 2, 3, 1 → 3출발 2경유 1도착
i, j, k ▶ 2, 3, 2 → 3출발 2경유 2도착
i, j, k ▶ 2, 3, 1 → 3출발 2경유 3도착
i, j, k ▶ 3, 1, 1 → 1출발 3경유 1도착
i, j, k ▶ 3, 1, 2 → 1출발 3경유 2도착
i, j, k ▶ 3, 1, 3 → 1출발 3경유 3도착
i, j, k ▶ 3, 2, 1 → 2출발 3경유 1도착
i, j, k ▶ 3, 2, 2 → 2출발 3경유 2도착
i, j, k ▶ 3, 2, 3 → 2출발 3경유 3도착
i, j, k ▶ 3, 3, 1 → 3출발 3경유 1도착
i, j, k ▶ 3, 3, 2 → 3출발 3경유 2도착
i, j, k ▶ 3, 3, 3 → 3출발 3경유 3도착
빨간색의 경우 확인하지 않아도 되어서 조건을 걸어주었다.
사실 조건을 안걸어줘도 정답을 잘 나온다.
코드
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
private static final int INF = 100 * 100_000;
static int n, m;
static int arr[][];
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
m = Integer.parseInt(br.readLine());
arr = new int[n + 1][n + 1];
// 입력받은 값으로 변수 초기화
init();
// 플로이드 와샬 알고리즘
floydWarshall();
// 출력을 위한 객체
StringBuilder sb = new StringBuilder();
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
// 무한대가 저장되어 있으면 경로가 없다는 의미이므로
// 0을 출력한다.
sb.append((arr[i][j] != INF ? arr[i][j] : 0) + " ");
}
sb.append("\n");
}
bw.write(sb.toString());
bw.close();
br.close();
}
private static void init() throws IOException {
for(int i = 0 ; i <= n; i++)
Arrays.fill(arr[i], INF);
// 자기 자신으로 가는 경로 0으로 초기화
for(int i = 1; i <= n; i++)
arr[i][i] = 0;
for(int i = 0 ; i < m; i++){
StringTokenizer st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
int cost = Integer.parseInt(st.nextToken());
// 기존의 값보다 작은 값이 들어오면 갱신해준다.
arr[start][end] = Math.min(cost, arr[start][end]);
}
}
private static void floydWarshall() {
// 경유 노드
for(int i = 1; i <= n; i++){
// 시작 노드
for(int j = 1; j <= n; j++){
// 시작 노드 != 경유 노드
if(i != j) {
// 도착 노드
for (int k = 1; k <= n; k++) {
// 시작노드 != 도착노드 && 도착노드 != 경유노드
if(j != k && i != k){
// 기존의 값 보다 경유한 값이 작은 경우
if(arr[j][k] > arr[j][i] + arr[i][k]){
arr[j][k] = arr[j][i] + arr[i][k];
}
}
}
}
}
}
}
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준 11657 : JAVA] 타임머신 / 벨만-포드 (0) | 2020.02.12 |
---|---|
[백준 1865 : JAVA] 웜홀/ 벨만-포드 (3) | 2020.02.12 |
[백준 9370 : JAVA] 미확인 도착지 / 다익스트라 (3) | 2020.02.09 |
[백준 1504 : JAVA] 특정한 최단 경로 / 다익스트라 (0) | 2020.02.09 |
[백준 1753 : JAVA] 최단경로 / 다익스트라 (0) | 2020.02.09 |