DSA DSA In Depth
0%
lesson bellman-fordnegative-cyclesdifference-constraints

Negative Weights: Bellman-Ford, Negative Cycle Reconstruction & Systems of Difference Constraints

7 min read Module 5.2: Disjoint Sets, Minimum Spanning Trees & Shortest Paths Updated Oct 23, 2026
📄 Cheat Sheet
Bellman-Ford Algorithm — Single Source Shortest Path & Negative Cycles
William Fiset Bellman-Ford Algorithm — Single Source Shortest Path & Negative Cycles Watch on YouTube ↗

Explains the V-1 edge relaxation bound, negative cycle detection on the V-th pass, and why negative cycles make shortest paths undefined.

0:00 / --:--

The Path-Relaxation Property & Negative Cycle Witness

Let a shortest simple path from $s$ to $v$ be $P = \langle v_0, v_1, \dots, v_k \rangle$ where $v_0 = s$, $v_k = v$, and $k \le |V| - 1$.

Formal Theorem — Path-Relaxation Invariant (CLRS Lemma 22.15)

If $G = (V, E)$ contains no negative-weight cycles reachable from $s$, then after $i$ complete passes of relaxing every edge in $E$, for every vertex $v$ that has a shortest path from $s$ consisting of at most $i$ edges, $d[v] = \delta(s, v)$.

Corollary (Negative Cycle Certificate on Pass $V$): Since every simple path has $\le |V| - 1$ edges, if no negative cycle is reachable from $s$, no distance $d[v]$ can decrease on the $|V|$-th pass. Conversely, if any edge $(u, v)$ satisfies $d[u] + w(u, v) < d[v]$ on pass $|V|$, then $v$ is reachable from a negative-weight cycle! Walking backward $|V|$ steps along $\text{parent}[\cdot]$ pointers from $v$ is guaranteed to land strictly inside the negative cycle itself.

1. Reconstructing the Exact Negative Cycle

Notice a subtle trap: if vertex $v$ is relaxed on pass $V$, $v$ might not lie on the negative cycle itself—it might merely be a downstream vertex reachable from a negative cycle $C$. However, because the predecessor subgraph traced backward from $v$ has out-degree $1$ in the reversed sense and was updated within the last $V$ steps, executing for (int i = 0; i < V; ++i) x = parent[x]; walks backward out of the downstream tail and is guaranteed to place $x$ on the cycle $C$. From there, following $\text{parent}$ until returning to $x$ extracts the exact cycle.

2. Systems of Difference Constraints ($x_v - x_u \le w$)

Suppose a scheduling or VLSI layout problem gives $M$ linear inequalities of the form:

$$x_v - x_u \le w_{u,v} \iff x_v \le x_u + w_{u,v}$$

Compare this directly with the shortest-path triangle inequality $\delta(s, v) \le \delta(s, u) + w(u, v)$! By creating a directed edge $(u, v)$ of weight $w_{u,v}$ for each constraint $x_v - x_u \le w_{u,v}$ and adding a virtual source $s^*$ with $0$-weight edges to all variables, the system of inequalities has a feasible solution if and only if the constraint graph has no negative cycle, and setting $x_v = \delta(s^*, v)$ is a valid solution!

Pass V Relaxation Witness & Backward V-Step Negative Cycle Extraction Negative Cycle (Sum = -2) 1 2 3 +2 +1 -5 4 v=5 Relaxed on pass V! Walk parent[v] V times → lands in {1,2,3}
flowchart LR Init["Initialize dist[s] = 0, others = INF"] --> Loop["Run V-1 relaxation passes over all E edges"] Loop --> Early{"Any edge relaxed in pass?"} Early -- "No" --> Done["Converged Early: Optimal Distances Ready"] Early -- "Yes (after V-1 passes)" --> PassV["Run V-th pass: check if dist[u] + w < dist[v]"] PassV -- "No improvement" --> Done PassV -- "Improved at v" --> Trace["Walk x = parent[x] V times, then extract cycle"]
Bellman-Ford Negative-Cycle Witness, Floyd-Warshall k-Pivot & Johnson Reweighting
O(VE) Bellman-Ford · O(V³) Floyd · O(VE log V) Johnson

Witness negative cycles on the V-th relaxation round of Bellman-Ford, inspect Floyd-Warshall’s in-place DP matrix D^(k)[i][j] = min(D[i][j], D[i][k]+D[k][j]), and reweight negative edges to non-negative via Johnson potentials w_hat(u,v) = w(u,v) + h(u) - h(v) ≥ 0.

Step 1 / 8
Custom Input:
Timeline

Invariant & State

Rounds 1..V-1 (1..3): Relaxed all edges. Current distances d = [0, 1, 2, -1]. If no negative cycle exists, all shortest paths are settled.
w=4 w=-2 w=1 w=-1 v0 d=0 v1 d=1 v2 d=2 v3 d=-1
Bellman-Ford Pass
Round V-1 = 3
Negative Cycle
Checking...
Diff Constraints
Feasible so far
[Step 1] Rounds 1..V-1 (1..3): Relaxed all edges. Current distances d = [0, 1, 2, -1]. If no negative cycle exists, all shortest paths are settled.
Hardware Reality — Flat Edge-Struct Array vs. Adjacency Lists in Bellman-Ford, and the SPFA Anti-Pattern

Because Bellman-Ford relaxes every edge indiscriminately on each pass, iterating over a single contiguous std::vector<Edge> of 16-byte {u, v, w} structs is $3\times$ faster than nested adjacency list loops. Conversely, the Queue-based Bellman-Ford variant (SPFA) has worst-case $O(V \cdot E)$ complexity and is easily forced into $O(V \cdot E)$ with terrible random-access cache misses by grid/ladder anti-SPFA test cases.

Complexity Matrix

Algorithm / Problem Average / Best Case Worst-Case Time Space Complexity
Bellman-Ford (with Early Exit) O(k · E) ($k \le V$) O(V · E) O(V)
SPFA (Queue-Based Moore) O(E) (random graphs) O(V · E) (adversarial grids) O(V)
Difference Constraints ($M$ inequalities) O(N + M) O(N · M) O(N + M)

Production Implementation: Bellman-Ford with Exact Negative Cycle Reconstruction

#include <vector>
#include <algorithm>

struct BFEdge {
    int u, v;
    long long weight;
};

// Finds any negative cycle in the graph (initializing dist to 0 acts as a virtual super-source!)
std::vector<int> findNegativeCycle(int n, const std::vector<BFEdge>& edges) {
    std::vector<long long> dist(n, 0);
    std::vector<int> parent(n, -1);
    int lastRelaxed = -1;

    for (int step = 0; step < n; ++step) {
        lastRelaxed = -1;
        for (const auto& e : edges) {
            if (dist[e.u] + e.weight < dist[e.v]) {
                dist[e.v] = dist[e.u] + e.weight;
                parent[e.v] = e.u;
                lastRelaxed = e.v;
            }
        }
        if (lastRelaxed == -1) return {}; // Early exit: no negative cycle exists
    }

    // Walk backward n steps to guarantee landing strictly inside the negative cycle
    int cycleNode = lastRelaxed;
    for (int i = 0; i < n; ++i) {
        cycleNode = parent[cycleNode];
    }

    std::vector<int> cycle;
    for (int cur = cycleNode;; cur = parent[cur]) {
        cycle.push_back(cur);
        if (cur == cycleNode && cycle.size() > 1) break;
    }
    std::reverse(cycle.begin(), cycle.end());
    return cycle;
}
Failure Mode — relaxing from INF Causes Signed Overflow with Negative Edges

When finding single-source shortest paths from $s$ (where unreachable vertices start at dist[u] = INF), if edge $(u, v)$ has weight $-10$ and $u$ is unreachable ($d[u] = \text{INF}$), evaluating dist[u] + (-10) < dist[v] will evaluate to INF - 10 < INF (True!), propagating fake finite distances through unreachable components! Always guard single-source relaxations with if (dist[e.u] != INF && dist[e.u] + e.weight < dist[e.v]).

Check yourself Question 1 of 4
Concept Check Why Bellman-Ford Runs Exactly V - 1 Passes recall
Section: Relaxation Invariant

In a graph with $V$ vertices and no negative-weight cycles reachable from $s$, why are $V - 1$ full edge-relaxation passes always sufficient to find all shortest paths from $s$?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

Given a table of currency exchange rates $R(u, v) > 0$ (units of currency $v$ per unit of currency $u$), an arbitrage loop is a cycle $C$ where $\prod_{(u,v) \in C} R(u, v) > 1$. How do you reduce this to Bellman-Ford?

Key Takeaways

Bellman-Ford relaxes all E edges V - 1 times because any simple shortest path in a V-vertex graph contains at most V - 1 edges.
An edge relaxation on the V-th pass proves the existence of a negative cycle; walking parent[x] backward V times from the relaxed vertex is guaranteed to enter the cycle.
Initializing all dist[0..V-1] = 0 is equivalent to adding a virtual super-source with weight-0 edges to every vertex, detecting negative cycles anywhere in a disconnected graph.
A system of difference constraints x_v - x_u <= w is feasible iff the graph with edges (u -> v, weight w) has no negative cycle.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue