Plain Dijkstra doesnβt directly apply here because the cheapest path might use more stops than a k-stop-respecting one β the stop limit changes which paths are even legal, so the state we search over must be (city, stops used so far), not just city. This is the same βresource-constrained shortest pathβ shape as Bellman-Fordβs round-by-round relaxation, which turns out to be exactly the right tool.
DFS with Memoization on (node, stops remaining)
Time O(V + E Β· k)Space O(V Β· k)Explore every path from src via DFS, tracking how many stops remain, and memoize on (current city, stops remaining) since the cheapest cost to reach dst from a given city with a given stop budget doesnβt depend on how you got there.
from collections import defaultdict
class Solution: def findCheapestPrice(self, n: int, flights: list[list[int]], src: int, dst: int, k: int) -> int: graph = defaultdict(list) for u, v, w in flights: graph[u].append((v, w))
memo = {}
def dfs(node: int, stops: int) -> float: if node == dst: return 0 if stops < 0: return float('inf') if (node, stops) in memo: return memo[(node, stops)]
best = float('inf') for nxt, price in graph[node]: cost = dfs(nxt, stops - 1) if cost != float('inf'): best = min(best, price + cost)
memo[(node, stops)] = best return best
result = dfs(src, k) return -1 if result == float('inf') else resultEach of the O(V Β· k) distinct (node, stops) states is computed once and reused; computing a state costs O(deg(node)), so total work is O(V Β· k + E Β· k) β O(E Β· k) time in dense graphs, with O(V Β· k) space for the memo table plus recursion stack.
Bellman-Ford, Limited to k + 1 Rounds
OptimalTime O(k Β· E)Space O(V)Bellman-Fordβs relaxation naturally models βat most k stopsβ: each round of relaxing every edge extends the reachable path length by exactly one edge. So running only k + 1 rounds (allowing up to k + 1 edges = at most k intermediate stops) directly enforces the stop limit, without needing to track (node, stops) pairs explicitly β the round number is the stop count.
The one subtlety: relax using a snapshot of the previous roundβs distances, not the array being updated in place. Otherwise a single round could chain multiple edge relaxations together, silently using more βhopsβ than that round is supposed to represent.
class Solution: def findCheapestPrice(self, n: int, flights: list[list[int]], src: int, dst: int, k: int) -> int: dist = [float('inf')] * n dist[src] = 0
for _ in range(k + 1): # k stops = k + 1 edges new_dist = dist[:] # snapshot: don't let this round chain onto itself for u, v, price in flights: if dist[u] != float('inf') and dist[u] + price < new_dist[v]: new_dist[v] = dist[u] + price dist = new_dist
return dist[dst] if dist[dst] != float('inf') else -1Tracing n=4, flights=[[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src=0, dst=3, k=1 (2 rounds = k + 1): watch the frozen snapshot. Every relaxation reads only those slate values, while each update lands in the separate building row below β so one round can never chain two edges into the same path, which is the whole reason 0β1β2β3 cannot sneak past the stop limit.
Frozen snapshot β every relaxation reads only these values
New distances β built one edge at a time
Edges β scanned in order
1/5 tested this roundRound 1 of 2 opens. The snapshot freezes the previous round result β `[0, β, β, β]` β and the new row below it is built one edge at a time. First edge `0β1`: the snapshot says `dist[0] = 0`, and `0 + 100 < β`, so `new[1] = 100` pops in (emerald). The snapshot itself never moves, because edges tested later this round must still see the old `β`.
After 2 rounds, dist[3] = 700 β matching the expected output. The tempting path 0β1β2β3 costs 100 + 100 + 200 = 400 β genuinely cheaper β but it uses 3 edges (2 stops), and a path only gains one edge per round from the fixed starting snapshot, so reaching it would need a third round that k = 1 forbids.
Why itβs correct: by induction, after round i, dist[v] holds the cheapest cost to reach v using at most i edges β a direct consequence of the Bellman-Ford invariant, here deliberately capped at k + 1 rounds instead of running to convergence (n - 1 rounds) specifically to enforce the stop constraint.
Complexity: k + 1 rounds, each scanning all E edges β O(k Β· E) time (and since k < n, this is also bounded by the more familiar O(n Β· E)). Space is O(V) for the two distance arrays, versus O(V Β· k) for the memoized DFS β the main advantage of this approach.