Shortest Paths II: Bellman-Ford, Floyd-Warshall, A*

Bellman-Ford, Floyd-Warshall, A*

Read: ~40 min Advanced 6 practice problems Live code runner

1. Beyond Dijkstra

Dijkstra is fast but breaks on negative edges. Bellman-Ford handles them in O(VE), and even detects negative cycles. Floyd-Warshall finds shortest paths between every pair of vertices in O(V³). A* uses a heuristic to focus search on promising directions, dramatically faster on geographic / grid problems.

Negative weight → Bellman-Ford। সব pair-এর shortest path দরকার → Floyd-Warshall। Goal-directed (যেমন pathfinding) → A*।

2. Bellman-Ford — V−1 Relaxation Rounds

Initialise dist[src] = 0, others = ∞. Relax every edge V − 1 times. After V − 1 rounds, shortest distances are final (a path uses ≤ V − 1 edges). One more relaxation round that still improves something proves a negative cycle.

bellman_ford.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    int V = 5;
    vector<tuple<int,int,int>> e = {
        {0,1, 6}, {0,2, 7}, {1,2, 8}, {1,3, 5},
        {1,4,-4}, {2,3,-3}, {2,4, 9}, {3,1,-2}, {4,3, 7}
    };
    vector<long long> dist(V, LLONG_MAX);
    dist[0] = 0;
    for (int i = 0; i < V - 1; i++)
        for (auto [u, v, w] : e)
            if (dist[u] != LLONG_MAX && dist[u] + w < dist[v])
                dist[v] = dist[u] + w;

    bool neg = false;
    for (auto [u, v, w] : e)
        if (dist[u] != LLONG_MAX && dist[u] + w < dist[v]) neg = true;

    if (neg) cout << "negative cycle\n";
    else for (int i = 0; i < V; i++)
        cout << "dist[0..." << i << "] = " << dist[i] << "\n";
}

3. Floyd-Warshall — All Pairs in O(V³)

Three nested loops on the V × V distance matrix:

for k in 0..V−1: for i: for j: dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

The order matters: k is the outer loop, representing "I'm now allowed to use intermediate vertices ≤ k". After all V iterations, dist[i][j] is the true shortest path.

floyd.cpp
#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e18;

int main() {
    int V = 4;
    vector<vector<long long>> d(V, vector<long long>(V, INF));
    for (int i = 0; i < V; i++) d[i][i] = 0;
    vector<tuple<int,int,int>> e = {{0,1,5},{0,3,10},{1,2,3},{2,3,1}};
    for (auto [u, v, w] : e) d[u][v] = w;

    for (int k = 0; k < V; k++)
        for (int i = 0; i < V; i++)
            for (int j = 0; j < V; j++)
                if (d[i][k] + d[k][j] < d[i][j])
                    d[i][j] = d[i][k] + d[k][j];

    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) cout << (d[i][j] == INF ? -1 : d[i][j]) << "\t";
        cout << "\n";
    }
}

4. Johnson's Algorithm — Negative-Edge All-Pairs

For sparse graphs with negative edges, Johnson's reweights edges via Bellman-Ford to non-negative, then runs Dijkstra from each vertex. Total: O(V·E·log V) — beats Floyd's V³ when E ≪ V². The reweighting trick uses node potentials.

5. A* — Heuristic Search

A* is Dijkstra with a heuristic h(v) estimating the remaining cost to the goal. Pop the vertex with smallest g(v) + h(v) (cost so far + heuristic). If h is admissible (never overestimates), A* finds the optimal path; if also consistent, no vertex is reopened.

Heuristic examples Grid pathfinding: Manhattan distance for 4-connectivity, Euclidean for diagonals. Sliding-puzzle: number of misplaced tiles, or Manhattan sum.
A* হলো Dijkstra-এর "intelligent" বড় ভাই। Goal-এর অনুমান (heuristic) থাকলে A* অসাধারণ দ্রুত কাজ করে — গেম AI, robot navigation, GPS routing-এর ভিত্তি।

6. Practice Problems

  1. Cheapest flights with at most K stops via bounded Bellman-Ford.
    K stops-এর মধ্যে cheapest flight — bounded Bellman-Ford।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: run K+1 relaxation rounds (instead of V−1). Use a previous dist array per round to avoid using more than K stops in one round.

  2. Detect arbitrage in currency exchange rates by negative-cycle detection.
    মুদ্রা arbitrage — negative cycle detection।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: for rate r(u→v), use weight −log(r). A cycle whose sum is negative means product of rates > 1 → arbitrage. Run Bellman-Ford and check the V-th relaxation.

  3. Transitive closure of a DAG (or any digraph) using Floyd-Warshall variant.
    Transitive closure — Floyd-Warshall।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: use boolean matrix; reach[i][j] |= reach[i][k] && reach[k][j]. O(V³). For V ≤ 1024, use std::bitset to speed up by 64×.

  4. Shortest path in a grid with non-negative weights using A* with Manhattan heuristic.
    Grid-এ A* — Manhattan heuristic।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: priority queue keyed by g + h. h(r,c) = |r − goalR| + |c − goalC|. Manhattan is admissible for 4-connected grids with unit costs.

  5. When would you choose Bellman-Ford over Dijkstra even on a non-negative graph?
    Non-negative graph-এ কখনো Bellman-Ford?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: rarely — Dijkstra is faster. But if the graph is dynamic and you need to add a single negative edge later, Bellman-Ford is naturally extensible. Also, Bellman-Ford is parallelisable across edges; Dijkstra's PQ is harder to parallelise.

  6. Why is the order k → i → j in Floyd-Warshall correct?
    Floyd-Warshall-এ লুপ-অর্ডার গুরুত্বপূর্ণ কেন?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: after the k-th outer iteration, dist[i][j] = shortest path using intermediate nodes only from {0, …, k}. Inductive proof: when we admit k as a new intermediate, the new shortest path either avoids k (already in dist[i][j]) or goes i → k → j (which is dist[i][k] + dist[k][j] computed in earlier iterations).

Summary — Module 28

Bellman-Ford handles negative edges in O(VE) and detects negative cycles. Floyd-Warshall answers all-pairs in O(V³). Johnson's reweights for sparse graphs. A* uses a heuristic to beat Dijkstra on goal-directed problems. Each tool has a niche — pick by graph size and edge weights.

Negative edge — Bellman-Ford। All pair — Floyd-Warshall। Heuristic available — A*। Tool box মাথায় রাখুন।

Next Module → Minimum Spanning Trees: Kruskal & Prim।