Algorithm/문제풀이

[LeetCode#787] Cheapest Flights Within K Stops

gilbert9172 2024. 9. 10. 14:48

❒ Description


제목 Cheapest Flights Within K Stops
링크 https://leetcode.com/problems/cheapest-flights-within-k-stops/description/
자료구조 비선형 (그래프) - 최단거리
시간 복잡도 O(ElogV)

다익스트라 알고리즘을 사용해서 풀 수 있는 문제이며, Comparable 인터페이스도 활용할 수 있다.
이 문제는 경유 횟수가 제한되어 있어서 굉장히 까다로웠다.

 

 

 

 

 

❒ Solution


1.  단순 solve

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
    //1. filghts 배열을 통해 graph 생성
    List<List<int[]>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
    for (int[] flight : flight) {
    	int departure = flight[0];
        int arrival = flight[1];
        int price = flight[2];
        graph.get(departure).add(new int[]{arrival, price});
    }
    
    //2. 방문 여부와 경유 횟수를 관리하기 윈한 Map 생성 
    Map<Integer, Integer> managingFlights = new HashMap<>();
    
    //3. 큐 생성 및 최초 값 삽입 (도착지, 비용, 경유횟수)
    Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(p -> p[1]));
    pq.add(new int[]{src, 0, 0});
    
    //4. 큐가 빌 때 까지 반복
    while (!pq.isEmpty()) {
        int[] cheapest = pq.poll();
        int arrival = cheapest[0], price = cheapest[1], viaCount = cheapest[2];
        
        // 반복하는 과정에서 현재 노드와 dst와 일치한다면 결과 반환
        if (arrival == dst) {
            return price;
        }
        
        // 5. 방문 처리
        visited.put(arrival, viaCount);
        
        // 6. 경유를 더 할 수 있다면, 
        if (viaCount < k) {
            viaCount += 1;
            // 7. 현재 노드에서 출발하는 모든 항공권 탐색하며 처리
            for (int[] node : graph.get(arrival)) {
                int nextNode = node[0];
                int nextPrice = node[1];
                if (!visited.containsKey(nextNode) || viaCount < visited.get(nextNode)) {
                    int totalPrice = price + nextPrice;
                    pq.add(new int[] {nextNode, totalPrice, viaCount});
                }
            }
        }
    }
    return -1;
}

 

 

!visited.containsKey(nextNode)

  • 처음 방문하는 노드라면 더 나은 경로가 없으므로 탐색을 계속한다.

 

viaCount < visited.get(nextNode)

  • visited.get(nextNode)는 해당 노드에 도달할 때 사용된 경유 횟수를 의미
  • viaCount는 현재 경로에서 nextNode에 도달할 때의 경유 횟수를 의미
  • viaCount가 이전에 기록했던 경유 횟수보다 적다면, 더 적은 경유로 해당 노드에 도달할 수 있다는 것을 의미
  • 만약 경유 횟수가 이전보다 크거나 같으면, 이미 더 나은 경로를 찾았기 때문에 굳이 다시 탐색할 필요X

 

 

 

2. Comparable 활용한 풀이

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
    //1. graph 생성
    List<List<int[]>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
    for (int[] flight : flights) {
        int idx = flight[0];
        int to = flight[1];
        int price = flight[2];
        graph.get(idx).add(new int[] {to, price});
    }
    //2. 가격이 낮은 순으로 하는 PQ 생성 & 진입점 삽입
    Queue<Flight> pq = new PriorityQueue<>();
    pq.add(new Flight(src, 0, 0));
    //3. 방문과 경유횟수를 관리해주는 맵 생성
    Map<Integer, Integer> visited = new HashMap<>();
    //4. PQ가 빌 때 까지 반복 수행
    while (!pq.isEmpty()) {
        Flight flight = pq.poll();
        int node = flight.departure, price = flight.price, viaCount = flight.viaCount;
        // ※ 만약 현재 노드가 최종 도착지라면 이때의 가격을 반환
        if (node == dst) {
            return price;
        }
        //5. 현재 노드를 방문 처리 (노드, 경유횟수)
        visited.put(node, viaCount);
        //6. 경유횟수 검증
        if (viaCount <= k) {
            //7. 경유횟수 1 증가
            viaCount += 1;
            //8. arrival을 출발지로 하는 경로가 있다면 처리
            for (int[] v : graph.get(node)) {
                int nextNode = v[0];
                int noextPrice = v[1];
                //9. 다음 노드를 최초로 방문하거나, 이전에 기록했던 경유 횟수보다 적을 경우  
                if (!visited.containsKey(nextNode) || viaCount < visited.get(nextNode)) {
                    int totalPrice = price + nextPrice;
                    pq.add(new Flight(nextNode, totalPrice, viaCount));
                }
            }
        }
    }
    return -1;
}

public static class Flight implements Comparable<Flight> {
    int departure;
    int price;
    int viaCount;

    public Flight(int departure, int price, int viaCount) {
        this.departure = departure;
        this.price = price;
        this.viaCount = viaCount;
    }

    @Override
    public int compareTo(Flight o) {
        return Integer.compare(this.price, o.price);
    }
}