There are n airports, labeled from 0 to n - 1, which are connected by some flights. You are given an array flights where flights[i] = [from_i, to_i, price_i] represents a one-way flight from airport from_i to airport to_i with cost price_i. You may assume there are no duplicate flights and no flights from an airport to itself.
You are also given three integers src, dst, and k where:
src is the starting airportdst is the destination airportsrc != dstk is the maximum number of stops you can make (not including src and dst)Return the cheapest price from src to dst with at most k stops, or return -1 if it is impossible.
Example 1:
Input: n = 4, flights = [[0,1,200],[1,2,100],[1,3,300],[2,3,100]], src = 0, dst = 3, k = 1
Output: 500Explanation:
The optimal path with at most 1 stop from airport 0 to 3 is shown in red, with total cost 200 + 300 = 500.
Note that the path [0 -> 1 -> 2 -> 3] costs only 400, and thus is cheaper, but it requires 2 stops, which is more than k.
Example 2:
Input: n = 3, flights = [[1,0,100],[1,2,200],[0,2,100]], src = 1, dst = 2, k = 1
Output: 200Explanation:
The optimal path with at most 1 stop from airport 1 to 2 is shown in red and has cost 200.
Constraints:
1 <= n <= 100fromi != toi1 <= pricei <= 10000 <= src, dst, k < n
You should aim for a solution as good or better than O(n + (m * k)) time and O(n) space, where n is the number of cities, m is the number of flights, and k is the number of stops.
Consider this as a graph problem where the cities are nodes and the flights are edges connecting two cities, with the ticket cost as the edge weight. Can you think of a shortest path algorithm to solve the problem? Perhaps a better algorithm than Dijkstra's that can intuitively handle the k stops condition.
We can use the Bellman-Ford algorithm. Initialize a prices array of size n with Infinity, setting prices[source] = 0. These values describe the cost to reach a city from the source city. Iterate (k + 1) times (stops are 0-indexed), updating the cost to each city by extending paths from cities with valid costs. We only update the cost for a city if it is less than the previous cost. How would you implement this?
At each level of iteration, we go through the given flights and use them to update the price array with the minimum costs compared to the previous level. We use a temporary prices array at each level to store the updated costs. After completing all levels, we return the result stored in prices[dst]. If that value is Infinity, we return -1 instead.
Before attempting this problem, you should be comfortable with:
We want the cheapest cost to go from src to dst, but we can take at most k stops (so at most k+1 flights/edges).
Normal Dijkstra finds the cheapest path, but it ignores stop limits.
So we treat a "state" as: (current city, how many stops used).
That way, reaching the same city with different stop counts is considered different, and we can enforce the limit.
We always expand the currently cheapest state first using a min-heap.
The first time we pop dst, that cost is the cheapest possible within the allowed stops.
u -> v with price w.dist[city][stopsUsed] = bestCost (initialize to infinity).(0, src, -1) into a min-heap, meaning cost 0 at src with "-1 stops so far" (so first flight makes stops = 0).(cost, city, stops).city == dst, return cost.stops == k, you can't take more flights, continue.city, stops+1) state, skip.nextCity, price):nextCost = cost + pricenextStops = stops + 1nextCost improves dist[nextCity][nextStops+1], update it and push into heap.dst, return -1.class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
INF = float("inf")
adj = [[] for _ in range(n)]
dist = [[INF] * (k + 5) for _ in range(n)]
for u, v, cst in flights:
adj[u].append([v, cst])
dist[src][0] = 0
minHeap = [(0, src, -1)] # cost, node, stops
while len(minHeap):
cst, node, stops = heapq.heappop(minHeap)
if dst == node: return cst
if stops == k or dist[node][stops + 1] < cst:
continue
for nei, w in adj[node]:
nextCst = cst + w
nextStops = 1 + stops
if dist[nei][nextStops + 1] > nextCst:
dist[nei][nextStops + 1] = nextCst
heapq.heappush(minHeap, (nextCst, nei, nextStops))
return -1Where is the number of cities, is the number of flights and is the number of stops.
We are allowed at most k stops, which means at most k + 1 flights (edges).
Bellman–Ford is perfect here because it relaxes edges level by level, where each iteration allows one more edge in the path.
Key idea:
i iterations, we know the cheapest cost to reach every city using at most i flights.k + 1 times, we ensure we only consider paths that respect the stop limit.prices array with infinity, set prices[src] = 0.k + 1 times:tmpPrices = prices.s -> d, cost):prices[s] is reachable:tmpPrices[d] = min(tmpPrices[d], prices[s] + cost)prices = tmpPrices.prices[dst] is still infinity, return -1.prices[dst].class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
prices = [float("inf")] * n
prices[src] = 0
for i in range(k + 1):
tmpPrices = prices.copy()
for s, d, p in flights: # s=source, d=dest, p=price
if prices[s] == float("inf"):
continue
if prices[s] + p < tmpPrices[d]:
tmpPrices[d] = prices[s] + p
prices = tmpPrices
return -1 if prices[dst] == float("inf") else prices[dst]Where is the number of cities, is the number of flights and is the number of stops.
This problem is still about finding the cheapest path with at most k stops.
SPFA (Shortest Path Faster Algorithm) is essentially a queue-optimized Bellman–Ford.
Key observations:
k stops.stops <= k.This approach works well because:
prices[i] = infinity for all cities.prices[src] = 0.currentCost, city, stopsUsed).(0, src, 0).cost, node, stops).stops > k, skip it.node -> neighbor, price):nextCost = cost + price.nextCost < prices[neighbor]:prices[neighbor].nextCost, neighbor, stops + 1) into the queue.prices[dst] is still infinity, return -1.prices[dst].class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
prices = [float("inf")] * n
prices[src] = 0
adj = [[] for _ in range(n)]
for u, v, cst in flights:
adj[u].append([v, cst])
q = deque([(0, src, 0)])
while q:
cst, node, stops = q.popleft()
if stops > k:
continue
for nei, w in adj[node]:
nextCost = cst + w
if nextCost < prices[nei]:
prices[nei] = nextCost
q.append((nextCost, nei, stops + 1))
return prices[dst] if prices[dst] != float("inf") else -1Where is the number of cities, is the number of flights and is the number of stops.
The problem allows at most k stops, which means at most k+1 flights. A common mistake is running Bellman-Ford for k iterations instead of k+1, or checking stops > k when you should check stops == k before taking another flight.
# Wrong: only k iterations (k flights, not k+1)
for i in range(k):
# Correct: k+1 iterations for k stops
for i in range(k + 1):When relaxing edges, using the same array for both reading and writing allows paths from the current iteration to chain together, potentially exceeding the allowed number of flights in a single round.
# Wrong: updates affect same iteration
if prices[s] + p < prices[d]:
prices[d] = prices[s] + p
# Correct: use temporary array
if prices[s] + p < tmpPrices[d]:
tmpPrices[d] = prices[s] + pStandard Dijkstra finds the cheapest path but ignores the stop constraint. A path with fewer stops might be more expensive but still valid, while the cheapest path might exceed k stops. The state must include both cost and number of stops used.
# Wrong: only tracks cost
dist[city] = cost
# Correct: track cost at each stop level
dist[city][stops] = cost