[백준/자바] 11404 플로이드
📌 문제
n(2 ≤ n ≤ 100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1 ≤ m ≤ 100,000)개의 버스가 있다. 각 버스는 한 번 사용할 때 필요한 비용이 있다.
모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.
⚔ 입력
첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비용은 100,000보다 작거나 같은 자연수이다.
시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.
📣 출력
n개의 줄을 출력해야 한다. i번째 줄에 출력하는 j번째 숫자는 도시 i에서 j로 가는데 필요한 최소 비용이다. 만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다.
💎 문제분석하기
플로이드 워셜 알고리즘의 기본 문제이다
플로이드 워셜의 핵심은 A -> C 노드 까지 최단 경라고 한다면
A - B
B - C
경로 역시 최단 경로라는 것이다
이와 같은 원리에 따라 플로이드 워셜의 핵심 로직이 파생된다
거리 z = A - C = start - end = D[start][end]
거리 y = A - B = start - k = D[start][k]
거리 x = B - C = k - end = D[k][end]
=> min(z, y+x)
=> 즉, 최단 거리 z값 보다 큰 상황이라면 작은 값으로 업데이트 해줌으로써 최단 거리를 업데이트할 수 있다
플로이드 워셜은 인접 행렬로 그래프를 표현한다
1) start와 end가 같은 값은 0으로, 나머지는 INF로 초기화 한다
2) 최단 거리 배열에 그래프 데이터를 저장한다 (가중치 = 거리 업데이트)
3) A, B, C에 대해서 3중의 반복문을 돌면서 핵심 로직으로 최단 거리 업데이트
=> 이때 중간 위치인 B(일반적으로 k로 표현)가 가장 바깥에 위치해야 한다
💡 코드 구현하기
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
private static final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
static int numberOfCities;
static int numberOfRoutes;
static int[][] mapWithDistance;
public static void main(String[] args) throws IOException {
receiveInfo();
initializeMap();
saveDistances();
floydWarshallAlgorithm();
drawAnswer();
}
private static void receiveInfo() throws IOException {
numberOfCities = Integer.parseInt(bufferedReader.readLine());
numberOfRoutes = Integer.parseInt(bufferedReader.readLine());
mapWithDistance = new int[numberOfCities + 1][numberOfCities + 1];
}
private static void initializeMap() {
for (int i = 1; i <= numberOfCities; i++) {
for (int j = 1; j <= numberOfCities; j++) {
if (i == j)
mapWithDistance[i][j] = 0;
else
mapWithDistance[i][j] = 10000001; // INF로 설정
}
}
}
private static void saveDistances() throws IOException {
for (int i = 0; i < numberOfRoutes; i++) {
StringTokenizer stringTokenizer = new StringTokenizer(bufferedReader.readLine());
int nodeDeparting = Integer.parseInt(stringTokenizer.nextToken());
int nodeArriving = Integer.parseInt(stringTokenizer.nextToken());
int distance = Integer.parseInt(stringTokenizer.nextToken());
if (isInitialDistanceShorter(nodeDeparting, nodeArriving, distance)) {
mapWithDistance[nodeDeparting][nodeArriving] = distance;
}
}
}
private static void floydWarshallAlgorithm() {
for (int k = 1; k <= numberOfCities; k++) { // 플로이드 워셜 알고리즘 수행
for (int i = 1; i <= numberOfCities; i++) {
for (int j = 1; j <= numberOfCities; j++) {
if (isAnotherShorterDistanceExist(k, i, j))
mapWithDistance[i][j] = mapWithDistance[i][k] + mapWithDistance[k][j];
}
}
}
}
private static boolean isAnotherShorterDistanceExist(int k, int i, int j) {
return mapWithDistance[i][j] > mapWithDistance[i][k] + mapWithDistance[k][j];
}
private static boolean isInitialDistanceShorter(int nodeDeparting, int nodeArriving, int distance) {
return mapWithDistance[nodeDeparting][nodeArriving] > distance;
}
private static void drawAnswer() {
for (int i = 1; i <= numberOfCities; i++) {
for (int j = 1; j <= numberOfCities; j++) { // 맵을 순회 = i, j 배열
if (mapWithDistance[i][j] == 10000001) { // 업데이트가 되지 않았다는 것은 최소 거리가 없음을 의미 = 갈수 없음을 의미
System.out.print("0 ");
} else {
System.out.print(mapWithDistance[i][j] + " ");
}
}
System.out.println();
}
}
}
풀이 참고 : Do it! 알고리즘 코딩테스트 - 자바 편
반응형
'알고리즘 > 백준' 카테고리의 다른 글
[백준/파이썬] 2953 나는 요리사다 (0) | 2022.11.28 |
---|---|
[백준/자바] 11050 이항 계수 구하기 1 (0) | 2022.11.27 |
[백준/자바] 1948 임계경로 (0) | 2022.11.26 |
[백준/자바] 1717 집합 표현하기 (0) | 2022.11.25 |
[백준/자바] K번째 수 (0) | 2022.11.24 |