자바 코딩테스트 강좌, 가장 큰 정사각형 찾기

문제 설명

2차원 배열이 주어졌을 때, 1로 구성된 가장 큰 정사각형의 넓이를 구하는 문제입니다. 배열의 각 요소는 0 또는 1의 값을 가지며, 1은 해당 위치가 채워져 있음을 나타냅니다. 예를 들어, 다음과 같은 배열이 주어졌다고 가정해 보겠습니다:

    0  1  1  0
    1  1  1  1
    0  1  1  0
    0  1  1  1
    

이 경우 가장 큰 정사각형의 크기는 3이며, 넓이는 9입니다.

입력 형식

입력은 m x n 크기의 2차원 배열로 주어집니다. 이 배열에서 i번째 행, j번째 열의 값이 배열의 셀을 나타냅니다.

출력 형식

가장 큰 정사각형의 넓이를 정수로 반환합니다.

접근법

이 문제는 동적 프로그래밍(Dynamic Programming)을 이용하여 해결할 수 있습니다. 동적 프로그래밍의 기본 아이디어는 이전에 계산된 결과를 사용하여 새로운 결과를 효율적으로 계산하는 것입니다. 다음과 같이 진행됩니다:

  1. 새로운 2차원 DP 배열을 생성합니다. DP[i][j]는 (i, j) 위치에서 정사각형의 최대 변의 길이를 나타냅니다.
  2. 배열의 요소를 순회하며, 만약 matrix[i][j]가 1이라면, DP[i][j]는 다음의 수식을 따릅니다:
  3. DP[i][j] = min(DP[i-1][j], DP[i][j-1], DP[i-1][j-1]) + 1
  4. 단, i와 j가 0일 때는 DP[i][j]는 matrix[i][j]의 값과 같아야 합니다.
  5. 최대 변의 길이를 기억하고, 이를 통해 최대 넓이를 계산합니다.

구현


public class LargestSquare {
    public int maximalSquare(char[][] matrix) {
        if (matrix == null || matrix.length == 0) return 0;
        
        int maxSide = 0;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int[][] dp = new int[rows][cols];
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == '1') {
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1; // 첫 행 또는 첫 열
                    } else {
                        dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                    }
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }
        
        return maxSide * maxSide;
    }
}

    

시간 복잡도

이 알고리즘의 시간 복잡도는 O(m * n)입니다. 여기서 m은 행의 수, n은 열의 수입니다. 배열을 한 번 순회하기 때문에 매우 효율적입니다.

공간 복잡도

DP 배열을 사용하기 때문에 공간 복잡도는 O(m * n)입니다. 하지만, 이전 행의 결과만 필요하기 때문에 공간을 최적화 할 수 있습니다.

최적화 방법

DP 배열 대신 한 행의 결과만 저장하여 공간 복잡도를 O(n)으로 줄일 수 있습니다. 다음은 이러한 최적화를 적용한 코드입니다:


public class OptimizedLargestSquare {
    public int maximalSquare(char[][] matrix) {
        if (matrix == null || matrix.length == 0) return 0;

        int maxSide = 0;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int[] dp = new int[cols + 1];
        int prev = 0;

        for (int i = 0; i < rows; i++) {
            for (int j = 1; j <= cols; j++) {
                int temp = dp[j]; // 현재 DP[j] 값을 저장
                if (matrix[i][j - 1] == '1') {
                    dp[j] = Math.min(Math.min(dp[j], dp[j - 1]), prev) + 1;
                    maxSide = Math.max(maxSide, dp[j]);
                } else {
                    dp[j] = 0; // 1이 아닌 경우
                }
                prev = temp; // 이전 DP[j] 값을 저장
            }
        }

        return maxSide * maxSide;
    }
}

    

마무리

가장 큰 정사각형 찾기 문제는 동적 프로그래밍의 중요성을 잘 보여주는 문제입니다. 이 문제를 통해 알고리즘 문제 해결 능력을 키우고, 코딩 테스트에서 자주 출제되는 패턴을 연습할 수 있습니다.

팁: 이 문제는 자주 출제되는 주제이므로 여러 차원에서 접근해 보시는 것을 추천드립니다. 기초부터 차근차근 연습하시기 바랍니다.

자바 코딩테스트 강좌, 가장 빠른 버스 노선 구하기

이번 글에서는 자바를 사용하여 코딩 테스트에서 출제될 수 있는 “가장 빠른 버스 노선 구하기” 문제를 다루어 보겠습니다. 버스를 이용한 최단 경로 문제는 그리디 알고리즘과 그래프 이론의 기본 개념을 익힐 수 있는 훌륭한 예제입니다. 이를 통해 알고리즘 문제 해결 능력을 향상시키고 자바 코딩 능력을 키워보도록 하겠습니다.

문제 설명

주어진 도시 A와 도시 B 사이에 여러 개의 버스 노선이 있으며, 각 버스 노선은 특정 시간에 출발하여 특정 시간에 도착하는 정보를 가지고 있습니다. 버스 노선은 상이한 소요 시간과 요금을 가지고 있고, 수 많은 노선 중에서 가장 빠른 노선을 선택해야 합니다.

입력

입력은 다음과 같은 형식으로 주어집니다:

  • 첫 번째 줄에 도시 A와 도시 B의 정보를 받습니다.
  • 두 번째 줄에 각 버스 노선의 수 N이 주어집니다.
  • 이어지는 N개의 줄에는 각각의 버스 노선의 시작 도시, 도착 도시, 소요 시간, 요금이 주어집니다.

출력

가장 빠른 버스에 대한 소요 시간을 출력합니다. 만약 도착할 수 없는 경우 “도착할 수 없습니다.”라고 출력합니다.

예제

    입력:
    A B
    3
    A B 5 3000
    A B 3 2500
    B A 2 1500

    출력:
    3
    

문제 풀이 과정

1. 문제 이해

가장 먼저 해야 할 일은 문제를 철저히 이해하는 것입니다. 노선이 여러 개 존재하며, 각 노선마다 출발지와 도착지, 소요 시간, 요금이 모두 다르다는 점에 유의해야 합니다. 이 문제는 최단 경로를 찾는 문제로 정리할 수 있습니다. 시작 도시 A에서 도착 도시 B로 가는 최단 경로를 찾아야 합니다.

2. 알고리즘 선택

이 문제를 풀기 위해 가장 적합한 알고리즘은 다익스트라(Dijkstra)의 최단 경로 알고리즘입니다. 이는 가중 그래프에서 두 정점 간의 최단 경로를 찾아주는 효율성이 높은 알고리즘으로, 여기서의 가중치는 소요 시간을 나타냅니다. 또한, 자바를 사용해서 구현할 수 있습니다.

3. 다익스트라 알고리즘 구현

다음은 자바로 다익스트라 알고리즘을 구현하는 과정입니다. 먼저, 필요한 클래스를 정의하고, 각 도시와 노선 정보를 저장하기 위한 자료구조를 설정하겠습니다.

    import java.util.*;

    public class BusRoute {
        static class Route {
            String destination;
            int time;
            int price;

            public Route(String destination, int time, int price) {
                this.destination = destination;
                this.time = time;
                this.price = price;
            }
        }

        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            String start = sc.nextLine(); // 출발지
            String end = sc.nextLine();   // 도착지
            int N = sc.nextInt();         // 노선 수
            Map> graph = new HashMap<>();

            for (int i = 0; i < N; i++) {
                String from = sc.next();
                String to = sc.next();
                int time = sc.nextInt();
                int price = sc.nextInt();
                graph.computeIfAbsent(from, k -> new ArrayList<>()).add(new Route(to, time, price));
            }

            // 다익스트라 알고리즘 호출
            int shortestTime = dijkstra(start, end, graph);

            // 결과 출력
            if (shortestTime == Integer.MAX_VALUE) {
                System.out.println("도착할 수 없습니다.");
            } else {
                System.out.println(shortestTime);
            }
        }
    }
    

4. 다익스트라 알고리즘의 핵심 로직

다익스트라 알고리즘을 구현하기 위해, 우선 우선순위 큐를 사용하여 최단 시간 정보와 도시 정보를 저장합니다. 각 도시에서 연결된 도시를 탐색하면서, 현재까지의 소요 시간이 기록된 배열을 기반으로 업데이트합니다. 다음은 다익스트라 함수 구현입니다.

    public static int dijkstra(String start, String end, Map> graph) {
        PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(r -> r.time));
        Map minTime = new HashMap<>();

        pq.add(new Route(start, 0, 0)); // 시작 도시 추가
        minTime.put(start, 0);

        while (!pq.isEmpty()) {
            Route current = pq.poll();

            // 도착지에 도달한 경우
            if (current.destination.equals(end)) {
                return current.time;
            }

            // 현재 도시와 연결된 노선 탐색
            for (Route route : graph.getOrDefault(current.destination, Collections.emptyList())) {
                int newTime = current.time + route.time;

                if (newTime < minTime.getOrDefault(route.destination, Integer.MAX_VALUE)) {
                    minTime.put(route.destination, newTime);
                    pq.add(new Route(route.destination, newTime, route.price));
                }
            }
        }

        return Integer.MAX_VALUE; // 도착할 수 없는 경우
    }
    

5. 코드 설명

위 코드에서 다익스트라 알고리즘은 우선순위 큐를 사용하여 가장 빠른 노선을 우선적으로 검토합니다. 소요 시간이 가장 적은 도시를 선택하고, 해당 도시와 연결된 다른 도시로의 이동을 진행합니다. 새로운 소요 시간이 기존에 기록된 시간보다 짧을 경우, 정보를 업데이트하며 앞으로 탐색을 이어갑니다.

결론

이번 포스트에서는 “가장 빠른 버스 노선 구하기” 문제를 통해 다익스트라 알고리즘을 자바로 구현하는 과정을 살펴보았습니다. 알고리즘 문제를 풀 때는 문제의 본질을 이해하고, 효과적인 알고리즘을 선택하는 것이 중요합니다. 이 예제는 코딩 테스트 준비에 매우 유용하며, 다른 유사한 문제들에서도 적용할 수 있는 기술입니다.

이와 같은 방식으로 복잡한 문제를 단계적으로 쪼개 해결해 나가며, 알고리즘적 사고를 키워나가길 바랍니다. 다음에는 더욱 다양한 알고리즘과 문제 해결 방법을 탐구해보도록 하겠습니다!

© 2023 블로그 작성자

자바 코딩테스트 강좌, 가장 길게 증가하는 부분 수열 찾기

안녕하세요! 오늘은 자바 코딩 테스트에서 자주 출제되는 문제 중 하나인 ‘가장 길게 증가하는 부분 수열(LIS: Longest Increasing Subsequence)’ 찾기 문제를 다루어보려 합니다. 이 문제는 주어진 수열에서 선택된 원소들의 값이 증가하는 가장 긴 부분 수열의 길이를 찾는 것입니다. 복잡한 알고리즘 문제처럼 보일 수 있지만, 효율적인 접근 방법을 통해 해결할 수 있으며, 이를 통해 코딩 테스트에서 점수를 향상시킬 수 있습니다.

문제 설명

주어진 정수 배열이 있습니다. 이 배열에서 가장 길게 증가하는 부분 수열을 찾아 그 길이를 반환하는 프로그램을 작성하세요. 예를 들어:

입력: [10, 9, 2, 5, 3, 7, 101, 18]
출력: 4
설명: 증가하는 부분 수열은 [2, 3, 7, 101]로 길이가 4입니다.

문제 해결 방법

이 문제를 해결하기 위해 여러 가지 접근 방식을 사용할 수 있습니다. 가장 직관적인 방법 중 하나는 동적 프로그래밍(Dynamic Programming) 기법을 사용하는 것입니다. 이 방법을 사용하면 O(n^2)의 시간복잡도로 문제를 해결할 수 있습니다. 또한, 이 문제를 더욱 최적화하면 O(n log n) 시간복잡도로도 해결할 수 있습니다. 이번 글에서는 두 가지 방법을 모두 다루어보겠습니다.

1. 동적 프로그래밍을 사용한 방법

먼저, 동적 프로그래밍을 사용하여 이 문제를 해결하는 방법을 살펴보겠습니다. 이 방법은 다음과 같은 절차로 진행됩니다:

  1. 길이를 n인 배열이 주어질 때, n 크기의 DP 배열을 생성합니다. DP 배열의 각 원소는 해당 인덱스까지의 가장 긴 증가하는 부분 수열의 길이를 저장합니다.
  2. DP 배열의 초기값으로 모두 1을 설정합니다. (각 원소는 최소한 자기 자신으로만 구성된 부분 수열을 가질 수 있기 때문입니다.)
  3. 이중 루프를 이용하여 각 원소를 확인하고, 이전 원소들과 비교하여 증가하는 부분 수열의 길이를 업데이트합니다.
  4. 최종적으로 DP 배열에서 가장 큰 값을 반환합니다.

이제 이를 자바로 구현해 봅시다.

public class LongestIncreasingSubsequence {

    public static int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        int n = nums.length;
        int[] dp = new int[n];
        
        // 모든 수열의 길이를 1로 초기화
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }
        
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }
        
        // DP 배열에서 최대값 찾기
        int maxLength = 0;
        for (int length : dp) {
            maxLength = Math.max(maxLength, length);
        }
        
        return maxLength;
    }

    public static void main(String[] args) {
        int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
        System.out.println("가장 길게 증가하는 부분 수열의 길이: " + lengthOfLIS(nums));
    }
}

코드 분석

위의 코드는 다음과 같은 단계로 진행됩니다:

  • 주어진 배열이 null이거나 길이가 0이면 0을 반환합니다.
  • DP 배열을 초기화하여 모든 값이 1이 되도록 설정합니다.
  • 이중 루프를 통해 각 원소를 비교하여 증가하는 부분 수열의 길이를 업데이트합니다.
  • 최종적으로 DP 배열에서 가장 긴 수열의 길이를 찾아 반환합니다.

2. O(n log n) 방법

다음으로, O(n log n) 방법을 살펴보겠습니다. 이 방법은 이진 탐색(Binary Search)을 통해 보다 효율적으로 문제를 해결할 수 있습니다. 이 알고리즘은 다음과 같은 절차를 따릅니다:

  1. 비어있는 리스트를 생성합니다.
  2. 입력 배열의 각 요소를 순회합니다. 리스트의 마지막 원소보다 큰 경우, 해당 원소를 리스트에 추가합니다.
  3. 리스트의 마지막 원소보다 작거나 같은 경우, 이진 탐색을 통해 리스트에서 해당 원소가 들어갈 위치를 찾은 후 그 위치의 원소를 대체합니다.
  4. 리스트의 길이가 결국 가장 길게 증가하는 부분 수열의 길이가 됩니다.

이제 이를 자바로 구현해 보겠습니다.

import java.util.ArrayList;
import java.util.List;

public class LongestIncreasingSubsequenceBinarySearch {

    public static int lengthOfLIS(int[] nums) {
        List lis = new ArrayList<>();
        
        for (int num : nums) {
            int index = binarySearch(lis, num);
            if (index == lis.size()) {
                lis.add(num);
            } else {
                lis.set(index, num);
            }
        }
        
        return lis.size();
    }

    private static int binarySearch(List lis, int num) {
        int left = 0, right = lis.size();

        while (left < right) {
            int mid = left + (right - left) / 2;
            if (lis.get(mid) < num) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        
        return left;
    }

    public static void main(String[] args) {
        int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
        System.out.println("가장 길게 증가하는 부분 수열의 길이: " + lengthOfLIS(nums));
    }
}

코드 분석

위의 코드 분석은 다음과 같습니다:

  • 빈 리스트를 생성하여 가장 길게 증가하는 부분 수열을 저장합니다.
  • 각 원소를 순회하며 이진 탐색을 통해 들어갈 위치를 찾습니다.
  • 리스트의 마지막 원소보다 작은 경우 해당 위치의 원소를 대체합니다.
  • 최종적으로 리스트의 길이를 반환하여 가장 길게 증가하는 부분 수열의 길이를 제공합니다.

결론

오늘은 자바를 사용하여 ‘가장 길게 증가하는 부분 수열’ 문제를 해결하는 두 가지 방법을 알아보았습니다. 동적 프로그래밍을 사용한 O(n^2) 방법과 이진 탐색을 활용한 O(n log n) 방법을 통해 문제를 해결할 수 있었습니다. 이 두 가지 접근법을 통해 코딩 테스트에서 유용하게 활용할 수 있을 것입니다.

여러분이 취업 준비와 코딩 테스트 준비에 도움이 되길 바랍니다. 감사합니다!

자바 코딩테스트 강좌, ‘좋은 수’ 구하기

문제 설명

주어진 수 N이 있을 때, N을 수의 자릿수의 합이 N보다 작도록 변환할 수 있는 컴퓨터 프로그램을 작성하시오.
여기서, 수의 자릿수의 합이 N보다 작으면 그 수를 ‘좋은 수’라고 부릅니다.
즉, 주어진 N보다 작은 모든 수 중에서 자릿수의 합이 N보다 작으면서, 가장 큰 수를 찾는 알고리즘 문제입니다.

문제 예시

예를 들어, N이 23일 경우:

  • 22 (2 + 2 = 4) → ‘좋은 수’
  • 21 (2 + 1 = 3) → ‘좋은 수’
  • 20 (2 + 0 = 2) → ‘좋은 수’
  • 19 (1 + 9 = 10) → ‘좋은 수’
  • 18 (1 + 8 = 9) → ‘좋은 수’
  • 17 (1 + 7 = 8) → ‘좋은 수’
  • 16 (1 + 6 = 7) → ‘좋은 수’
  • 15 (1 + 5 = 6) → ‘좋은 수’
  • 14 (1 + 4 = 5) → ‘좋은 수’
  • 13 (1 + 3 = 4) → ‘좋은 수’
  • 12 (1 + 2 = 3) → ‘좋은 수’
  • 11 (1 + 1 = 2) → ‘좋은 수’
  • 10 (1 + 0 = 1) → ‘좋은 수’
  • 9 (9 = 9) → ‘좋은 수’

가장 큰 ‘좋은 수’는 22입니다.

문제 해결을 위한 접근 방법

이 문제를 해결하기 위해 먼저, 입력으로 주어진 N보다 1 작은 수부터 내려가며 각 수의 자릿수의 합을 계산합니다.
해당 자릿수의 합이 N보다 작을 경우, 그 수를 결과로 반환하고 알고리즘을 종료하는 방식입니다.

알고리즘 구현

        
        public class GoodNumber {
            public static void main(String[] args) {
                int N = 23; // 여기에서 테스트할 N 값을 설정합니다.
                int result = findGoodNumber(N);
                System.out.println("‘좋은 수’는: " + result);
            }

            public static int findGoodNumber(int N) {
                for (int i = N - 1; i > 0; i--) {
                    if (digitSum(i) < N) {
                        return i; // 가장 큰 ‘좋은 수’를 반환
                    }
                }
                return -1; // 만약 찾지 못하면 -1 반환
            }

            public static int digitSum(int num) {
                int sum = 0;
                while (num > 0) {
                    sum += num % 10; // 각 자릿수를 더함
                    num /= 10; // 10으로 나누어 자릿수를 줄임
                }
                return sum;
            }
        }
        
        

코드 설명

위의 자바 코드에서는 메인 메소드에서 N값을 설정한 후, findGoodNumber 메소드를 호출하여 ‘좋은 수’를 찾아냅니다.
findGoodNumber 메소드는 N보다 1 작은 수부터 시작하여 자릿수의 합을 계산하고, 그 값이 N보다 작으면 그 수를 반환합니다.

digitSum 메소드는 주어진 숫자의 각 자릿수의 합을 계산하는 로직을 포함하고 있습니다.
이 메소드는 반복문을 사용하여 자릿수를 하나씩 분리하고 더한 결과를 반환합니다.

시간 복잡도 분석

이 알고리즘의 시간 복잡도는 O(N * D)입니다. 여기서 N은 입력값, D는 N의 자릿수입니다.
각 수에 대해 자릿수를 sum하는 데 O(D)의 연산이 필요하게 되며, N까지 내려가며 반복하기 때문에 최악의 경우 O(N * D)의 시간 복잡도를 가집니다.

공간 복잡도

공간 복잡도는 O(1)입니다. 추가적인 공간을 사용하지 않고, 입출력 변수들과 메소드 내부의 상수적인 공간만 사용하므로
사용되는 메모리는 최소화되어 있습니다.

결론 및 팁

‘좋은 수’ 구하기 문제는 단순한 반복문과 자릿수 합 과정을 통해 그 해를 쉽게 찾을 수 있습니다.
그러나 N의 크기가 매우 클 경우 성능 저하가 우려되므로 알고리즘 최적화가 필요할 수 있습니다.
다양한 입력값에 대해 테스트 해보며, 다른 방법이나 구조로 이 문제를 해결해보는 것도 좋은 학습이 될 것입니다.

마지막으로, 코딩테스트를 준비하면서 이러한 문제를 충분히 연습해보는 것이 중요합니다.
매번 새로운 문제에 도전하며 다양한 사고력을 기르는 것은 취업 준비에 큰 도움이 될 것입니다.

자바 코딩테스트 강좌, K번째 최단 경로 찾기

K번째 최단 경로 찾기는 그래프 이론에서 많이 다루는 문제 중 하나입니다. 주어진 두 정점 사이에 K번째로 짧은 경로를 찾는 문제로,
그래프의 특성을 잘 이해하고 적절한 알고리즘을 적용하는 것이 중요합니다. 이 글에서는 이 문제의 정의와 해결 방법,
자바 코드 구현에 대해 자세히 설명하겠습니다.

문제 정의

주어진 방향 그래프에 대해 두 정점 A와 B 사이의 K번째 최단 경로를 찾는 문제를 고려해 봅니다.
이 때 “최단 경로”란 각 경로의 가중치 합이 최소인 경로를 의미합니다. K번째 최단 경로는 가장 짧은 경로를 첫 번째로 두고,
그 다음으로 짧은 경로, 그 다음 등을 차례로 비교하여 K번째를 찾는 것입니다.

입력

  • 정점의 개수 N (1 ≤ N ≤ 100)
  • 간선의 개수 M (1 ≤ M ≤ 500)
  • 각 간선의 정보 (시작 정점, 도착 정점, 가중치)
  • 시작 정점 A, 끝 정점 B, K

출력

정점 A에서 B까지의 K번째 최단 경로의 길이를 출력합니다.
K번째 최단 경로가 존재하지 않는다면 -1을 출력합니다.

알고리즘 개요

K번째 최단 경로 문제를 해결하기 위해 사용할 수 있는 알고리즘에는 Dijkstra 알고리즘의 변형이 있습니다.
일반적인 Dijkstra 알고리즘은 시작 정점에서 다른 모든 정점까지의 최단 경로를 찾습니다.
이를 K번째 최단 경로를 찾기 위해 약간의 변형을 적용해야 합니다.

문제 해결 접근법

1. 우선순위 큐(Priority Queue)를 사용하여 각 정점에서 K번째 최단 경로를 저장합니다.
2. 각 정점에 대해 최단 경로를 리스트 형태로 유지하며, 우선순위 큐를 통해 경로를 탐색합니다.
3. 경로를 탐색하면서 K번째 최단 경로를 갱신합니다.

단계별 설명

  • 우선순위 큐 초기화: 각 정점에 대해 최단 경로를 K개까지 저장할 수 있는 구조체를 만든다.
  • Dijkstra 알고리즘 수행: 시작 정점에서 출발하여 인접한 정점으로의 가중치 합을 계산하고 우선순위 큐에 추가한다.
  • K번째 경로 확인: B 정점까지 도달한 경우, K번째 경로의 길이를 확인한다.

자바 코드 구현

아래는 K번째 최단 경로를 찾는 자바 코드입니다:


import java.util.*;

class Edge {
    int to, weight;

    Edge(int to, int weight) {
        this.to = to;
        this.weight = weight;
    }
}

class KthShortestPath {
    static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int N = sc.nextInt(); // 정점 개수
        int M = sc.nextInt(); // 간선 개수
        int K = sc.nextInt(); // K

        List[] graph = new ArrayList[N + 1];
        for (int i = 1; i <= N; i++) {
            graph[i] = new ArrayList<>();
        }

        for (int i = 0; i < M; i++) {
            int u = sc.nextInt();
            int v = sc.nextInt();
            int w = sc.nextInt();
            graph[u].add(new Edge(v, w));
        }

        int start = sc.nextInt();
        int end = sc.nextInt();

        System.out.println(findKthShortestPath(graph, N, start, end, K));
    }

    static int findKthShortestPath(List[] graph, int N, int start, int end, int K) {
        PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
        List[] minHeap = new ArrayList[N + 1];

        for (int i = 1; i <= N; i++) {
            minHeap[i] = new ArrayList<>();
        }

        pq.add(new int[]{0, start}); // {길이, 정점}
        
        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int currLength = current[0];
            int currNode = current[1];

            minHeap[currNode].add(currLength);
            if (minHeap[currNode].size() > K) {
                continue;
            }

            for (Edge edge : graph[currNode]) {
                int nextNode = edge.to;
                int nextLength = currLength + edge.weight;

                pq.add(new int[]{nextLength, nextNode});
            }
        }

        if (minHeap[end].size() < K) {
            return -1;
        }
        return minHeap[end].get(K - 1);
    }
}

예제 입력 및 출력

입력 예제


5 7 3
1 2 3
1 3 5
2 3 1
2 4 6
3 4 2
4 5 1
3 5 8
1 5

출력 예제


9

결론

K번째 최단 경로 찾기는 그래프를 이해하고, 적절한 알고리즘을 선택하여 문제를 해결하는데 큰 도움이 됩니다.
Dijkstra 알고리즘을 변형하여 K번째 최단 경로를 찾는 방법을 익히면 다양한 코딩 테스트에서 측면에서 유용하게 활용할 수 있습니다.

이 글을 통해 K번째 최단 경로 문제를 이해하고 자바로 구현하는 방법을 배웠습니다.
앞으로도 다양한 알고리즘 문제를 풀며 실력을 쌓아가시기 바랍니다!