You are given n
cities numbered from 0
to n-1
and a list of flights, where each flight is represented as [from, to, price]
. Your task is to find the cheapest price from a given source city src
to a destination city dst
with at most K
stops (where a stop is a transfer at an intermediate city). If there is no such route, return -1
.
K
.1 <= n <= 100
0 <= flights.length <= (n * (n - 1))
flights[i] = [fromi, toi, pricei]
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 10^4
0 <= src, dst, K < n
At first glance, this problem looks like a variation of the shortest path problem in a weighted directed graph, but with the extra constraint of a maximum number of stops (K
). A brute-force approach would be to try all possible paths from src
to dst
with at most K
stops and pick the one with the lowest cost. However, this quickly becomes infeasible as the number of cities and flights grows.
To optimize, we need a way to systematically explore possible routes while keeping track of both the cost and the number of stops. Traditional shortest path algorithms like Dijkstra’s do not work directly because they do not account for the "number of stops" constraint. Instead, a modified Breadth-First Search (BFS) or a priority queue-based approach (similar to Dijkstra’s but with an extra state for stops) can be used. This allows us to explore all possible paths up to K
stops efficiently and find the minimum cost.
We can solve this problem using a variation of Dijkstra's algorithm with a priority queue (min-heap), where each state in the queue keeps track of:
K
, skip this state.-1
.
Example:
n = 4
, flights = [[0,1,100],[1,2,100],[2,3,100],[0,2,500]]
, src = 0
, dst = 3
, K = 1
-1
.
If K = 2
, the path 0 → 1 → 2 → 3 is possible with cost 300 and 2 stops.
Brute-force Approach:
Tries all possible paths from src
to dst
with at most K
stops. In the worst case, this is exponential: O(n^{K+1})
time, which is infeasible for large n
or K
.
Optimized Priority Queue Approach:
O(E * K)
where E
is the number of flights. Each node can be processed up to K
times (once for each possible number of stops).
O(n * K)
for the queue and visited states.
The key to solving "Cheapest Flights Within K Stops" is recognizing it as a constrained shortest path problem. By using a priority queue and tracking both cost and number of stops, we efficiently explore all viable routes without exceeding the stop limit. The solution is elegant because it combines the strengths of BFS and Dijkstra’s algorithm, ensuring optimality while respecting the problem’s unique constraints.
import heapq
from collections import defaultdict
class Solution:
def findCheapestPrice(self, n, flights, src, dst, K):
graph = defaultdict(list)
for u, v, w in flights:
graph[u].append((v, w))
heap = [(0, src, 0)] # (cost, city, stops)
visited = dict() # (city, stops): cost
while heap:
cost, city, stops = heapq.heappop(heap)
if city == dst:
return cost
if stops > K:
continue
for nei, price in graph[city]:
if (nei, stops) not in visited or cost + price < visited[(nei, stops)]:
visited[(nei, stops)] = cost + price
heapq.heappush(heap, (cost + price, nei, stops + 1))
return -1
#include <vector>
#include <queue>
#include <unordered_map>
#include <limits>
using namespace std;
class Solution {
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
vector<vector<pair<int, int>>> graph(n);
for (auto& f : flights)
graph[f[0]].emplace_back(f[1], f[2]);
// min-heap: (cost, city, stops)
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
pq.emplace(0, src, 0);
unordered_map<int, int> visited; // key: city * (K+2) + stops
while (!pq.empty()) {
auto [cost, city, stops] = pq.top(); pq.pop();
if (city == dst) return cost;
if (stops > K) continue;
for (auto& [nei, price] : graph[city]) {
int key = nei * (K+2) + stops;
if (!visited.count(key) || cost + price < visited[key]) {
visited[key] = cost + price;
pq.emplace(cost + price, nei, stops + 1);
}
}
}
return -1;
}
};
import java.util.*;
class Solution {
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
Map<Integer, List<int[]>> graph = new HashMap<>();
for (int[] f : flights) {
graph.computeIfAbsent(f[0], x -> new ArrayList<>()).add(new int[]{f[1], f[2]});
}
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
pq.offer(new int[]{0, src, 0}); // {cost, city, stops}
Map<String, Integer> visited = new HashMap<>();
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int cost = curr[0], city = curr[1], stops = curr[2];
if (city == dst) return cost;
if (stops > K) continue;
if (!graph.containsKey(city)) continue;
for (int[] nei : graph.get(city)) {
String key = nei[0] + "," + stops;
if (!visited.containsKey(key) || cost + nei[1] < visited.get(key)) {
visited.put(key, cost + nei[1]);
pq.offer(new int[]{cost + nei[1], nei[0], stops + 1});
}
}
}
return -1;
}
}
/**
* @param {number} n
* @param {number[][]} flights
* @param {number} src
* @param {number} dst
* @param {number} K
* @return {number}
*/
var findCheapestPrice = function(n, flights, src, dst, K) {
const graph = Array.from({length: n}, () => []);
for (const [u, v, w] of flights) {
graph[u].push([v, w]);
}
const heap = [[0, src, 0]]; // [cost, city, stops]
const visited = new Map();
while (heap.length) {
heap.sort((a, b) => a[0] - b[0]);
const [cost, city, stops] = heap.shift();
if (city === dst) return cost;
if (stops > K) continue;
for (const [nei, price] of graph[city]) {
const key = nei + ',' + stops;
if (!visited.has(key) || cost + price < visited.get(key)) {
visited.set(key, cost + price);
heap.push([cost + price, nei, stops + 1]);
}
}
}
return -1;
};