DSA sheet / Graph

Shortest Routes I

medium about 35 min Graph shortest path (Dijkstra)dijkstraweighted-graphpriority-queue
Open on CSES

Do these first: Message Route

The problem in brief

There are n cities (up to 100 000) and m one-way flights (up to 200 000), each with a positive length up to 10^9. Print the length of the shortest route from city 1 to each city 1..n.

Try it

Tab indents; press Esc, then Tab, to leave the box. Ctrl+Enter runs.

Hints

Stuck? Reveal one hint at a time. Each nudges without giving away the next.

The walkthrough

Spoilers ahead: open a section only after you have given the hints a fair try.

How to think

This is single-source shortest paths with non-negative weights: the domain of Dijkstra’s algorithm. The brute-force alternative, trying every path, is exponential, and Bellman-Ford (relax all edges n times) is O(n * m), too slow at this size. Dijkstra works because of one invariant: the unsettled node with the smallest tentative distance has that distance final, since any other route to it would have to pass through some unsettled node with an even larger tentative distance and then add non-negative cost. This is exactly why the algorithm breaks with negative edges.

Implementation choices matter. A min-heap keyed by distance gives O((n + m) log n). Instead of decreasing keys, push a new (distance, node) pair whenever you improve a distance and skip stale pairs when you pop them (if popped distance is larger than the stored one, ignore). The habit: when a problem has weights, the first question is “are they non-negative?”, and that decides Dijkstra versus Bellman-Ford.

Intuition

Imagine cities lighting up in order of how cheaply you can reach them. The next city to light up is always the cheapest of all cities adjacent to the lit region. When a city lights up its outgoing flights offer new, possibly cheaper prices to its neighbours.

Approach
  1. dist[1] = 0, all others infinity; push (0, 1) on a min-heap.
  2. Pop the smallest (d, u). If d > dist[u], skip it (stale). Otherwise for each flight u -> v with price w: if d + w < dist[v], update dist[v] and push (dist[v], v).
  3. When the heap is empty, print dist[1..n]. The code below assumes every city can be reached from city 1 (the statement does not spell this out, so this sheet assumes every test is reachable); on an input with an unreachable city these programs would print their infinity marker, so decide what to print there before relying on them.

Pitfalls: distances can reach about 10^5 * 10^9 = 10^14, so use 64-bit; edges are directed, so add each only one way; use adjacency lists; in JavaScript there is no built-in heap, so the version below includes a small binary heap.

Complexity

O((n + m) log m) time, O(n + m) memory.

Solutions

Written from scratch and checked by compiling and running each one against a brute force on random inputs. Fast input/output, the way you would submit it.

Show solutions (C++17, Python 3, Java 17, Node.js)
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, long long>>> adj(n + 1);
    for (int i = 0; i < m; i++) {
        int a, b;
        long long w;
        cin >> a >> b >> w;
        adj[a].push_back({b, w});
    }
    const long long INF = LLONG_MAX / 4;
    vector<long long> dist(n + 1, INF);
    priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<>> pq;
    dist[1] = 0;
    pq.push({0, 1});
    while (!pq.empty()) {
        auto [d, u] = pq.top();
        pq.pop();
        if (d > dist[u]) continue;  // stale entry
        for (auto [v, w] : adj[u]) {
            if (d + w < dist[v]) {
                dist[v] = d + w;
                pq.push({dist[v], v});
            }
        }
    }
    for (int v = 1; v <= n; v++) cout << dist[v] << (v < n ? ' ' : '\n');
    return 0;
}

Problem: CSES Problem Set (Antti Laaksonen, University of Helsinki), CC BY-NC-SA 4.0. The explanations and code above are original (© Anupam Kumar).

Related problems

Back to the sheet