Leetcode 787. Cheapest Flights Within K Stops
You are given a series of flights flights
, where flights[i] = [fromi, toi, pricei]
indicates that there is a flight from city fromi
to city toi
with cost pricei
.
You are also given three integers src
, dst
, and k
. You need to find the cheapest price from the city src
to the city dst
with at most k
stops. If there is no such route, return -1
.
n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
200
n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
500
Q: Can cities be revisited? A: No, the same city cannot be revisited according to constraints of cycles in Dijkstra’s algorithm.
Q: Are there any limits on the number of cities or flights?
A: The constraints typically are given in the full problem statement on LeetCode. However, for optimal implementation, we can generally expect n
and flights
to be reasonably large but within computational limit for algorithms like DFS, BFS, or Dijkstra’s with priority queue.
Q: Is the graph directed or undirected?
A: The graph representing flights is a directed graph because a flight from A
to B
does not imply a flight from B
to A
.
We can approach this problem using a modified Dijkstra’s algorithm with a priority queue (min-heap). The priority queue will help efficiently fetch the current minimum cost route. Additionally, tracking the number of stops helps ensure the k
stop condition is met.
flights
.(cost, current_city, stops)
.(0, src, 0)
into the priority queue.k + 1
, push all neighboring cities into the priority queue with updated costs and stops.k
stops, return -1
.#include <vector>
#include <queue>
#include <unordered_map>
#include <tuple>
using namespace std;
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
// Create the adjacency list
unordered_map<int, vector<pair<int, int>>> graph;
for (const auto& flight : flights) {
graph[flight[0]].emplace_back(flight[1], flight[2]);
}
// Priority queue: (cost, current_node, stops)
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;
pq.emplace(0, src, 0);
// Vector to track minimum cost to reach each city with a given number of stops
vector<vector<int>> min_cost(n, vector<int>(k + 2, INT_MAX));
min_cost[src][0] = 0;
while (!pq.empty()) {
auto [cost, curr_city, stops] = pq.top();
pq.pop();
if (curr_city == dst) return cost;
// If we have more stops available
if (stops <= k) {
for (const auto& [neighbor, price] : graph[curr_city]) {
int new_cost = cost + price;
// If we find a cheaper cost path or if we're visiting the neighbor with fewer stops.
if (new_cost < min_cost[neighbor][stops + 1]) {
min_cost[neighbor][stops + 1] = new_cost;
pq.emplace(new_cost, neighbor, stops + 1);
}
}
}
}
return -1;
}
O(E)
, where E
is the number of edges (flights).push
and pop
operation in the priority queue is O(log(V + E))
. Over the entire algorithm, this results in O((V + E) log(V + E))
.Overall, the time complexity is O((V + E) log(V + E)), making the solution efficient even for relatively large inputs.
Got blindsided by a question you didn’t expect?
Spend too much time studying?
Or simply don’t have the time to go over all 3000 questions?