150NeetCode 150

98.Cheapest Flights Within K Stops

MediumGraphs · 19/19

Problem

There are n cities numbered 0 to n - 1 and a list of flights, where flights[i] = [from, to, price] is a one-way flight costing price.

Given src, dst and an integer k, return the cheapest price from src to dst using at most k stops (i.e. at most k + 1 flights). If no such route exists, return -1.

Examples

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
Explanation: 0 -> 1 -> 3 costs 700 with 1 stop. 0 -> 1 -> 2 -> 3 costs 400 but needs 2 stops.
Input:  n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Output: 200
Explanation: 0 -> 1 -> 2 uses 1 stop and costs 200.
Input:  n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
Output: 500
Explanation: With 0 stops only the direct flight is allowed.

Constraints

  • 1 <= n <= 100
  • 0 <= flights.length <= n * (n - 1) / 2
  • flights[i].length == 3, 0 <= from, to < n, from != to
  • 1 <= price <= 10^4
  • No multiple flights between the same pair of cities.
  • 0 <= src, dst, k < n, src != dst

💡 Key Insight

The stop limit breaks plain Dijkstra: the cheapest way to reach a city might use too many stops, and a pricier route with fewer stops could be the only one that still reaches dst in time. Bellman-Ford fits perfectly, because its i-th round computes the cheapest cost using at most i edges. Running exactly k + 1 rounds — and relaxing from a copy of the previous round's distances so one round never chains two flights — gives the answer directly.

Brute Force DFS

TimeO(d^(k+1))SpaceO(n + E)

Intuition

Try every route from src that uses at most k + 1 flights, tracking the cost so far, and keep the minimum cost seen on arrival at dst. Prune any branch whose cost already matches or exceeds the best answer.

Algorithm

  1. Build an adjacency list from -> (to, price).
  2. dfs(city, flightsLeft, cost): if city == dst, update best and return. If flightsLeft == 0 or cost >= best, return.
  3. For each outgoing flight, recurse with flightsLeft - 1 and cost + price.
  4. Return best, or -1 if it was never set.

Visualization

k = 1 (2 flights allowed), src = 0, dst = 3
0 -100-> 1 -600-> 3          cost 700  best = 700
0 -100-> 1 -100-> 2          no flights left, stop
best = 700

Code

java
import java.util.ArrayList;
import java.util.List;

class Solution {
    private List<List<int[]>> adj;
    private int best;
    private int dst;

    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        adj = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            adj.add(new ArrayList<>());
        }
        for (int[] f : flights) {
            adj.get(f[0]).add(new int[] {f[1], f[2]});
        }
        this.dst = dst;
        best = Integer.MAX_VALUE;
        dfs(src, k + 1, 0); // k stops = k + 1 flights
        return best == Integer.MAX_VALUE ? -1 : best;
    }

    private void dfs(int city, int flightsLeft, int cost) {
        if (city == dst) {
            best = Math.min(best, cost);
            return;
        }
        if (flightsLeft == 0 || cost >= best) {
            return; // out of flights or already too expensive
        }
        for (int[] f : adj.get(city)) {
            dfs(f[0], flightsLeft - 1, cost + f[1]);
        }
    }
}

Complexity

  • Time: O(d^(k+1)) — d is the max out-degree; every route of up to k + 1 flights may be explored.
  • Space: O(n + E) — adjacency list plus recursion depth k + 1.

Complexity Comparison

Approach Time Space
Brute Force DFS O(d^(k+1)) O(n + E)
Dijkstra with Stop Tracking O(E · k · log(E · k)) O(n + E · k)
Bellman-Ford with k + 1 Rounds O(k · E) O(n)

Common Pitfalls

  • Confusing stops with flights: k stops means up to k + 1 edges.
  • Relaxing Bellman-Ford in place, which lets one round use several edges and breaks the stop limit.
  • Using plain Dijkstra with a single dist[] and a visited set — it discards pricier-but-shorter routes that may be the only valid ones.
  • Adding price to INF and overflowing; always check prices[u] != INF first.