본문 바로가기

알고리즘/백준

[백준 1956 : JAVA] 운동 / 벨만-포드

개요

 

알고리즘 문제를 풀 때는 문제를 잘 읽고 어떤 알고리즘을 적용할 수 있을까?를 먼저 생각하는 편이 좋다.

 

이 문제는 1 ▶ 1, 2 ▶ 2, 3 ▶3 ..... n ▶n 으로 가는 최단거리 중 최솟값을 구하는 문제이다.

 

시작 노드가 모든 노드에 해당하는 값을 알아야 하기 때문에 다익스트라, 벨만 포드가 아닌 플로이드 와샬을 사용한다.

 

플로이드 와샬을 통해 모든 노드 ▶ 모든 노드 의 최단거리를 구한 뒤 사이클을 이루는 도로의 길이 중 최솟값을 구한다.

 

플로이드 와샬이 처음이라면 아래의 문제와 풀이를 먼저 풀고 이 문제를 푸는 것을 추천한다.

 

아래의 풀이에는 간략한 플로이드 와샬 알고리즘에 대한 설명이 있다.

 

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

 

11404번: 플로이드

첫째 줄에 도시의 개수 n(1 ≤ n ≤ 100)이 주어지고 둘째 줄에는 버스의 개수 m(1 ≤ m ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비용은 100,000보다 작거나 같은 자연수이다. 시작

www.acmicpc.net

2020/02/12 - [알고리즘/백준] - [백준 11404 : JAVA] 플로이드 / 플로이드 와샬

 

[백준 11404 : JAVA] 플로이드 / 플로이드 와샬

개요 문제 이름처럼 플로이드 와샬 알고리즘을 사용한다. 플로이드 와샬은 다익스트라, 벨만포드와 같이 간선의 가중치가 1이 아닌경우에 최단경로를 찾는데 사용된다. 각각의 특징을 짧게 나마 정리하면, 다익스..

dragon-h.tistory.com

문제

 

V개의 마을와 E개의 도로로 구성되어 있는 도시가 있다. 도로는 마을과 마을 사이에 놓여 있으며, 일방 통행 도로이다. 마을에는 편의상 1번부터 V번까지 번호가 매겨져 있다고 하자.

당신은 도로를 따라 운동을 하기 위한 경로를 찾으려고 한다. 운동을 한 후에는 다시 시작점으로 돌아오는 것이 좋기 때문에, 우리는 사이클을 찾기를 원한다. 단, 당신은 운동을 매우 귀찮아하므로, 사이클을 이루는 도로의 길이의 합이 최소가 되도록 찾으려고 한다.

도로의 정보가 주어졌을 때, 도로의 길이의 합이 가장 작은 사이클을 찾는 프로그램을 작성하시오. 두 마을을 왕복하는 경우도 사이클에 포함됨에 주의한다.

 

입력

 

풀이

 

1. 플로이드 와샬을 이용하여 모든 노드 ▶ 모든 노드 의 최단거리를 구한다.

  • 3중 for문을 이용한다.
  • 각각의 for문은 바깥쪽부터 경유 노드, 시작 노드, 도착 노드에 해당한다.

2. 출발점과 도착점이 같은 경우(운동을 하고 돌아와야 하기 때문)의 값 중 최솟값을 구한다.

  • 만약 최솟값이 INF라면 경로가 없으므로 -1을 출력한다.

 

코드

 

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));
    // 무한대를 뜻한다. 경로가 없다는 뜻
    // |V - 1| * 최대 거리 가 최대 거리가 된다.
    private static final int INF = 400 * 10_000;
    static int v, e, result = INF;
    static int arr[][];

    public static void main(String[] args) throws IOException {
        StringTokenizer st = new StringTokenizer(br.readLine());

        v = Integer.parseInt(st.nextToken());
        e = Integer.parseInt(st.nextToken());
        // 그래프 정보를 저장한다.
        arr = new int[v + 1][v + 1];
        // 초기에는 경로가 없는 것처럼 무한대로 설정한다.
        for(int i = 0; i <= v; i++)
            Arrays.fill(arr[i], INF);

        // 입력 값에 따른 그래프 갱신
        for(int i = 0 ; i < e; i++){
            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            int dist = Integer.parseInt(st.nextToken());

            arr[start][end] = Math.min(arr[start][end], dist);
        }

        // 플로이드 와샬
        for(int i = 1; i <= v; i++){
            for(int j = 1; j <= v; j++){
                for(int k = 1; k <= v; k++){
                    if(arr[j][k] > arr[j][i] + arr[i][k]){
                        arr[j][k] = arr[j][i] + arr[i][k];
                    }
                }
            }
        }

        // 최소 사이클 중 최솟값을 구한다.
        for(int i = 1 ; i <= v; i++)
            result = Math.min(arr[i][i], result);

        // 최솟값이 INF 라면 경로가 없다는 뜻이다.
        if(result == INF) bw.write("-1\n");
        // 최솟값이 INF가 아니라면 경로가 있다는 뜻이므로 최솟값을 출력한다.
        else bw.write(result + "\n");

        bw.close();
        br.close();
    }
}