Cheapest Flights Within K Stops

Bellman-Ford with a boarding-pass budget: relax every flight once per pass, always reading last pass's fares, and after exactly k+1 passes the destination's fare is the cheapest route that respects the stop limit.

The memory hook

K stops is a budget of k+1 flights — run Bellman-Ford for exactly k+1 rounds, each round reading a snapshot of the last.

MediumBounded Bellman-FordO(k · E) timeO(n) space
01

K stops is an edge budget

intuition

Plain Dijkstra finds the cheapest route but counts no stops; DFS counts stops but enumerates everything. Notice what the constraint actually limits: edges. Bellman-Ford's outer loop is literally indexed by edges used — after pass i, dist[v] is the cheapest fare to v using at most i flights. The algorithm fits the problem exactly; you just stop it early, at k+1 passes instead of n−1.

1Convert the budgetA stop is a city between src and dst, so k stops allow k+1 flights. The fare table must never reflect more flights than that — the budget is an edge count, not a city count.
2Relax from a snapshotEach pass, copy dist into prev. Flight u→v improves v only if prev[u] + price beats dist[v] — reading the snapshot caps every route at one new flight per pass.
3Stop after k+1 passesPass i finalizes every route of ≤ i flights. After k+1 passes, dist[dst] is the answer — still ∞ means no route fits the budget, so return −1.
The one sentence to remember: the pass counter is the stop counter — each pass buys every city exactly one more flight, so nothing within the budget is missed and nothing beyond it is ever used.
02

Watch the budget get spent

playable trace

The canonical example hides a trap: ignoring stops, the cheapest route is 0→1→2→3 for 400 — but it burns 2 stops and the budget is k=1. Step through and watch the snapshot refuse to chain that route; the legal answer is 700.

Playable trace · Bounded Bellman-Ford
Frames are generated from the same logic as the code below.
Presets
Current event
Ready — press Play
Cheapest fare to destination
∞
Flights — every pass offers each one, in order
Only the source costs 0; every other fare is ∞. Press Play to start pass 1.
unreached (∞) edge being relaxed fare improved unreachable in budget answer
0 / 0
03

Bounded Bellman-Ford, k+1 passes

the solution
Python · Bounded Bellman-Ford
class Solution:
    def findCheapestPrice(self, n, flights, src, dst, k):
        INF = float('inf')
        dist = [INF] * n
        dist[src] = 0            # only the source costs 0 with 0 flights

        for _ in range(k + 1):         # k stops ⇒ k+1 flights allowed
            prev = dist[:]             # snapshot: this pass reads last pass only
            for u, v, price in flights:
                if prev[u] + price < dist[v]:
                    dist[v] = prev[u] + price   # relax: reach v via u

        return -1 if dist[dst] == INF else dist[dst]

An early exit (stop when a pass changes nothing) is a safe micro-optimization but proves nothing new — mention it, don't lead with it. Dijkstra also works if the state becomes (city, stops used); bounded Bellman-Ford is shorter to write and far easier to prove correct under a stop limit.

The budgetk stops ⇒ k+1 flightsA stop is an intermediate city, so the route src … dst may use at most k+1 edges. The fare table must never reflect more than that.
The snapshotprev = dist[:]Pass i reads only prev — the fares banked after pass i−1 — so one pass can never chain two flights. Updating in place would silently overspend the budget.
The relaxationdist[v] = min(dist[v], prev[u] + price)Flight u→v offers city v a candidate: reach u within i−1 flights, then take this one. Keep it only if it beats every offer seen so far.
Why does pass i never overspend? Induction on passes. After pass 0 only src is reachable — every fare uses ≤ 0 flights. If prev holds the best fares using ≤ i−1 flights, then any route to v of ≤ i flights is a ≤ (i−1)-flight route to some u plus one final flight u→v — and pass i tries every flight as that final leg, from prev. So after pass i, dist covers all ≤ i-flight routes and nothing longer. After k+1 passes, dist[dst] is the cheapest fare within budget; ∞ means impossible, return −1.
TimeO(k · E)k+1 passes, each scanning all E flights once. Since k < n, this is at most O(n · E) — trivially fast for n ≤ 100.
SpaceO(n)Two fare arrays of length n: dist and its snapshot prev. The flight list is input, not extra state.
04

Two quick mental checks

make it stick
n=4, src=0, dst=3, k=1
700

The trap case. The unrestricted cheapest route 0→1→2→3 costs 400 but needs 2 stops — over budget. Within 1 stop the best is 0→1→3 = 700, and the snapshot is exactly what forbids chaining 0→1 and 1→2 in a single pass.

n=3, src=0, dst=2, k=0
500

Zero stops means direct flights only, so only one pass runs. The 200 layover route never enters the table; the direct flight's 500 is the answer. Same input with k=1 returns 200 instead.

What to say out loud
"K stops is really an edge budget of k+1, and Bellman-Ford spends that budget one edge per pass. As long as each pass relaxes from a snapshot of the previous one, pass i can never use more than i flights — so after k+1 passes, dist[dst] is my answer, or −1 if it's still infinity."