2Brute2Best
🔥0

Cheapest Flights Within K Stops

Medium·LeetCode #787Basic Anchor
LeetCode
30-Second Mental ModelShortest Paths (weighted): Bellman-Ford with an edge limit
Complexity:O(N^(K + 1))→O(K · E)
Key Insight

Keep dist[c], the cheapest price to city c found so far (∞ if none). Repeat k + 1 times: copy dist into next, then go over every flight u → v with price w. If dist[u] + w < next[v], set next[v] = dist[u] + w. At the end of the round, dist = next. The answer is dist[dst]. The copy is what makes each round add exactly one flight.

Core Validity Invariant

A flight may extend only a price that was already in dist at the start of the round. So each round adds exactly one flight to a trip, and after round r every entry of dist is the cheapest price with at most r flights.

01

Problem

There are n cities, numbered 0 to n − 1, and a list of one-way flights. Each flight is [from, to, price]. You start in city src and want to fly to city dst.

Find the cheapest total price of a trip from src to dst that makes at most k stops. A stop is a city you land in between your start and your end, so k stops means at most k + 1 flights. If no trip fits, return -1.

Input

n (the number of cities), flights (a list of [from, to, price]), src, dst and k.

Output

The cheapest price from src to dst with at most k stops, or -1 if no such trip exists.

Example 1
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Output: 700

The trip 0 → 1 → 3 costs 100 + 600 = 700 and has one stop (city 1). The trip 0 → 1 → 2 → 3 costs only 400, but it has two stops, so it is not allowed.

Example 2
Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Output: 200

The trip 0 → 1 → 2 costs 100 + 100 = 200 and has one stop. The direct flight 0 → 2 costs 500.

Example 3
Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
Output: 500

With no stops you can only take the direct flight 0 → 2, which costs 500.

Constraints
  • • 2 ≤ n ≤ 100
  • • 0 ≤ flights.length ≤ n · (n − 1) / 2
  • • flights[i].length == 3
  • • 0 ≤ from, to < n, and from ≠ to
  • • 1 ≤ price ≤ 10^4
  • • There are never two flights between the same pair of cities.
  • • 0 ≤ src, dst, k < n
  • • src ≠ dst
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    With k = 1, how many flights can a trip use? Which cities can it land on in between?

  2. 2.

    Plain Dijkstra keeps one cheapest route per city. Can the cheapest route to a city also be the one that uses the most flights?

  3. 3.

    If I know the cheapest price to every city using at most 2 flights, how do I get the cheapest price using at most 3?

  4. 4.

    When I update a city's price during one pass over the flights, should a flight I just used in that same pass be allowed to extend the next flight?

  5. 5.

    How many passes over the flights do I need for k stops?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceDepth-first search over all trips with at most k + 1 flights; keep the cheapest that lands on dstO(N^(K + 1))O(N + E)
OptimalRelax every flight once per round for k + 1 rounds; each round reads last round's prices from a copyO(K · E)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “cheapest price”Each flight adds a price, so the total is a sum along the trip. This is a weighted shortest-path question.
  • “at most k stops”A limit on the number of flights. Dijkstra can't count flights, so the limit needs rounds.
  • “return -1 if no such trip”Some cities stay ∞ after the rounds, so they must be checked before returning.
weighted flights+at most k + 1 flights+one round per flight, reading from a copy→Shortest Paths (weighted): Bellman-Ford with an edge limit

Dijkstra fits 'cheapest price' but not 'at most k stops': it keeps only the cheapest trip to each city, even when that trip uses too many flights. This question is the anchor of the Bellman-Ford sub-pattern. The rule that decides validity is a flight count, not a visited set or a cost check, so the rounds are what make the limit exact. The copy per round is the detail that must not be skipped.

10

Don't make this mistake

Plain Dijkstra that ignores the stop count
✗ // priority_queue of (cost, city): pop the cheapest city, relax its flights, stop when dst is popped
✓ for (int round = 0; round <= k; round++) { /* copy dist, relax every flight */ }

Dijkstra keeps one cheapest trip per city, even when it uses too many flights. On example 1 with k = 1 it settles city 2 at 200 (2 flights), then city 3 at 400 through it (3 flights). It returns 400, but the answer is 700. This is the pattern-confusion trap: the stop limit needs rounds, not a heap.

Relaxing in place
✗ dist[v] = dist[u] + price; // writes straight into dist, so the same round reads it again
✓ next[v] = dist[u] + price; // writes into the copy; reads still come from dist

One in-place round on example 1 already gives dist = [0, 100, 200, 400]. Flight 1 → 2 reads the 100 written a moment earlier, and flight 2 → 3 reads the 200. City 3 gets 400 in one round with three flights. With k = 1 the function returns 400 instead of 700.

Off by one on k
✗ for (int round = 0; round < k; round++) { ... }
✓ for (int round = 0; round <= k; round++) { ... }

k stops means k + 1 flights, so we need k + 1 rounds. On example 3 (k = 0) the wrong loop runs no rounds at all. dist[2] stays ∞, and the function returns -1 instead of 500.

Relaxing from a city that cannot be reached yet
✗ if (dist[u] + price < next[v]) next[v] = dist[u] + price; // INF = INT_MAX
✓ if (dist[u] != INF && dist[u] + price < next[v]) next[v] = dist[u] + price;

INT_MAX + price overflows. In C++ that is undefined behaviour, and in practice it wraps to a negative number. An unreachable city then looks cheaper than everything, so a fake price gets into the answer. Check dist[u] != INF first.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Shortest Paths (Weighted) translate into production architectures and low-level memory mechanics.