코딩 테스트(Coding Test)/백준

[백준] 1753번 : 최단경로 - 자바(Java)

다문다뭉 2024. 11. 22. 11:58

Problem 🔒

문제

방향그래프가 주어지면 주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구하는 프로그램을 작성하시오. 단, 모든 간선의 가중치는 10 이하의 자연수이다.

입력

첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1 ≤ V ≤ 20,000, 1 ≤ E ≤ 300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1 ≤ K ≤ V)가 주어진다. 셋째 줄부터 E개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수 (u, v, w)가 순서대로 주어진다. 이는 u에서 v로 가는 가중치 w인 간선이 존재한다는 뜻이다. u와 v는 서로 다르며 w는 10 이하의 자연수이다. 서로 다른 두 정점 사이에 여러 개의 간선이 존재할 수도 있음에 유의한다.

출력

첫째 줄부터 V개의 줄에 걸쳐, i번째 줄에 i번 정점으로의 최단 경로의 경로값을 출력한다. 시작점 자신은 0으로 출력하고, 경로가 존재하지 않는 경우에는 INF를 출력하면 된다.

더보기

예제 입력 1

5 6
1
5 1 1
1 2 2
1 3 3
2 3 4
2 4 5
3 4 6

예제 출력 1

0
2
3
7
INF

Approach ⭕

  1. 서로 다른 두 정점 사이에 여러 개의 간선이 존재한다?
    • 다익스트라 알고리즘은 각 정점에 도달하는 최소 비용만 저장한다.
    • 그래서 두 정점 사이에 여러 간선이 있더라도, 그중 비용이 가장 작은 간선만 영향을 미친다.
    • 따라서 다익스트라 알고리즘을 사용하는 기본적인 유형의 문제이다.
  2. 다익스트라 알고리즘 동작 과정(우선순위 큐 활용)
    1. 각 노드에 대한 최소 비용 배열을 최댓값으로 초기화한다.
    2. 시작 정점은 비용을 0으로 설정한다.
    3. 우선순위 큐를 비용에 대해 오름차순 정렬로 생성한다.
    4. 큐에 시작 정점과 비용을 넣고, 큐가 빌 때까지 반복한다.
      • 큐에서 현재 정점과 현재 비용을 꺼낸다.
      • 큐에서 꺼낸 정점의 비용이 저장된 비용보다 크다면 무시한다.
      • 큐에서 꺼낸 정점에서 인접한 정점으로 이동하는데 걸리는 비용이 저장된 비용보다 작다면 갱신한다.
      • 갱신했다면, 큐에 인접한 정점과 갱신된 비용을 넣는다.

Solution 💡

public class 최단경로 {
    static int V, E, K, cost[];
    static ArrayList<ArrayList<int[]>> list = new ArrayList<>();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        V = Integer.parseInt(st.nextToken())+1;
        E = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(br.readLine());

        cost = new int[V];
        for(int i=0; i<V; i++){list.add(new ArrayList<>());}
        for(int i=0; i<E; i++){
            st = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            int w = Integer.parseInt(st.nextToken());
            list.get(u).add(new int[]{v,w});
        }
        Dijkstra(K);
        StringBuilder sb = new StringBuilder();
        for(int i=1; i<V; i++){
		    // cost배열에 초기값 MAZ_VALUE가 저장되어 있다는건 경로가 존재하지 않는다는 뜻
            sb.append(cost[i]==Integer.MAX_VALUE?"INF":cost[i]).append("\\n");
        }
        System.out.println(sb);
    }

    public static void Dijkstra(int start){
        PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->a[1]-b[1]);
        Arrays.fill(cost, Integer.MAX_VALUE);
        cost[start] = 0;
        q.add(new int[]{start, 0});

        while (!q.isEmpty()){
            int[] current = q.poll();
            int curNode = current[0];
            int curCost = current[1];
            // 큐에서 꺼낸 정점의 비용이 저장된 비용보다 크다면 무시
            if(cost[curNode]<curCost) continue;
            for(int[] n : list.get(curNode)){
                int nextNode = n[0];
                int nextCost = curCost + n[1]; // 인접한 노드로 이동하는데 걸리는 비용이
                // 저장된 비용보다 작다면 갱신하기
                if(nextCost < cost[nextNode]){
                    cost[nextNode] = nextCost;
                    q.add(new int[]{nextNode, nextCost});
                }
            }
        }
    }
}

알게된 점 😯

  • 다익스트라 알고리즘은 두 정점 사이에 여러 간선이 있더라도, 그 중 비용이 가장 작은 간선만 영향을 미치는구나 ?-?
  • 오늘도 1일 1알고리즘 Clear!