|
| 1 | +# Cheapest Flights Within K Stops |
| 2 | + |
| 3 | +You are given n cities, numbered from 0 to n−1 connected by several flights. You are also given an array flights, where |
| 4 | +each flight is represented as `flights[i]=[fromi ,toi, pricei]` meaning there is a direct flight from city |
| 5 | +`fromᵢ` to city `toᵢ` with a cost of `priceᵢ`. |
| 6 | + |
| 7 | +You are also given three integers: |
| 8 | + |
| 9 | +- src: The starting city. |
| 10 | +- dst: The destination city. |
| 11 | +- k: The maximum number of stops allowed on the route (i.e., intermediate cities between src and dst). |
| 12 | + |
| 13 | +Your task is to find the minimum possible cost to travel from src to dst using at most k stops (i.e., the route may |
| 14 | +contain up to k + 1 flights). If there is no valid route from src to dst that uses at most k stops, return −1. |
| 15 | + |
| 16 | +## Constraints |
| 17 | + |
| 18 | +- 2 <= n <= 100 |
| 19 | +- 0 <= flights.length <= (n * (n - 1) / 2) |
| 20 | +- flights[i].length == 3 |
| 21 | +- 0 <= `fromi`, `toi` < n |
| 22 | +- `fromi` != `toi` |
| 23 | +- 1 <= `pricei` <= 10^4 |
| 24 | +- There will not be any multiple flights between two cities. |
| 25 | +- 0 <= src, dst, k < n |
| 26 | +- src != dst |
| 27 | + |
| 28 | +## Examples |
| 29 | + |
| 30 | +Example 1 |
| 31 | +```text |
| 32 | +Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1 |
| 33 | +Output: 700 |
| 34 | +Explanation: |
| 35 | +The graph is shown above. |
| 36 | +The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700. |
| 37 | +Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops. |
| 38 | +``` |
| 39 | + |
| 40 | +Example 2 |
| 41 | +```text |
| 42 | +Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1 |
| 43 | +Output: 200 |
| 44 | +Explanation: |
| 45 | +The graph is shown above. |
| 46 | +The optimal path with at most 1 stop from city 0 to 2 is marked in red and has cost 100 + 100 = 200. |
| 47 | +``` |
| 48 | + |
| 49 | +Example 3 |
| 50 | +```text |
| 51 | +Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0 |
| 52 | +Output: 500 |
| 53 | +Explanation: |
| 54 | +The graph is shown above. |
| 55 | +The optimal path with no stops from city 0 to 2 is marked in red and has cost 500. |
| 56 | +``` |
| 57 | + |
| 58 | +## Topics |
| 59 | + |
| 60 | +- Dynamic Programming |
| 61 | +- Depth-First Search |
| 62 | +- Breadth-First Search |
| 63 | +- Graph Theory |
| 64 | +- Heap (Priority Queue) |
| 65 | +- Shortest Path |
| 66 | + |
| 67 | +## Solution |
| 68 | + |
| 69 | +The core intuition behind this solution is to treat the problem as a shortest path in a directed, weighted graph with a |
| 70 | +strict limit on the number of edges (flights), i.e., we are only allowed to use at most k stops, meaning at most k + 1 |
| 71 | +flights (edges). Traditional algorithms, such as Standard Dijkstra with a single dist[node], doesn’t enforce the stop |
| 72 | +bound because they always choose the globally cheapest path so far, even if that path exceeds the allowed number of stops. |
| 73 | +To correctly enforce the stop limit, we employ a Bellman–Ford–style dynamic programming approach, which naturally handles |
| 74 | +constraints on the number of edges in a path. |
| 75 | + |
| 76 | +The idea is to repeatedly relax all flights, exactly k + 1 times, where iteration t represents allowing routes that use |
| 77 | +up to t flights (one more than the previous iteration). After each iteration, we have the cheapest costs using at most r |
| 78 | +flights (edges). |
| 79 | + |
| 80 | +To achieve this, we maintain two arrays: `prices`, which stores the best costs found using up to t − 1 flights, and |
| 81 | +`temp_prices`, which stores the best costs for the current iteration t. During iteration t, every update to a path is |
| 82 | +made only from values in `prices`, not from updates made earlier in the same iteration. This separation ensures that any |
| 83 | +path discovered in iteration t uses at most one more flight than the paths from the previous iteration t − 1, preventing |
| 84 | +us from accidentally chaining multiple flights in the same round. |
| 85 | + |
| 86 | +The algorithm builds valid paths layer by layer, only extending shorter paths into longer ones, guaranteeing that when |
| 87 | +all k + 1 iterations are done, we have explored all possible routes that use at most k stops. And the cheapest such route |
| 88 | +will be stored in prices[dst]. |
| 89 | + |
| 90 | +Using the intuition above, we implement the algorithm as follows: |
| 91 | + |
| 92 | +1. Create an array `prices` of length n and initialize all its entries to infinity. |
| 93 | +2. Set `prices[src] = 0` because the cost to reach the starting city from itself is 0 and requires no flights. |
| 94 | +3. Iterate at most `k + 1` times to model the flight limit: |
| 95 | + - Initialize a new array, `temp_prices`, with a copy of the dist array. Copying ensures we can also keep the best |
| 96 | + older answers (using fewer flights) instead of forcing exactly t flights. |
| 97 | + - For each flight: |
| 98 | + - If `prices[u]` is not equal to infinity, and the candidate cost: `prices[u] + w` is less than the current |
| 99 | + `temp_prices[v]`: |
| 100 | + - Set `temp_prices[v]` to `prices[u] + w`. |
| 101 | + - After processing all flights in this iteration, set `prices` to `temp_prices`. |
| 102 | +4. After completing all `k + 1` iterations, check `prices[dst]`. If `prices[dst]` is still inf, it means there is no |
| 103 | + valid route from `src` to `dst` that uses at most `k` stops, so we return -1. Otherwise, return `prices[dst]`. |
| 104 | + |
| 105 | +### Time Complexity |
| 106 | + |
| 107 | +The time complexity of this algorithm is O((k+1)×m) because we perform k+1 relaxation rounds, and in each round, we |
| 108 | +iterate over all m flights once. Asymptotically, this simplifies to O(k×m). |
| 109 | + |
| 110 | +### Space Complexity |
| 111 | + |
| 112 | +The space complexity of this algorithm is O(n) because we maintain two arrays, dist and new_dist, each of size n (the |
| 113 | +number of cities). These arrays are reused across iterations, and no other auxiliary data structure grows with the input |
| 114 | +size. |
0 commit comments