Visual walkthrough — Bellman-Ford algorithm — DP approach, negative cycles detection, O(VE)
We assume nothing. If you have never seen a graph, a distance array, or the word "relax", start reading at line one — every symbol is earned before it is used.
Step 1 — What is the thing we are trying to compute?
WHAT. We have a set of cities (called vertices, drawn as dots) and one-way roads between them (called edges, drawn as arrows). Each road has a number written on it — the weight — the cost of driving down that road. A weight can be negative, meaning that road pays you money to drive it.
We pick one city as home, called the source and written . Our goal:
WHY define it this way. "Smallest total weight" is just adding up every road's number along a route. Because some numbers can be negative, a longer route (more roads) might cost less. That single fact is what makes this hard — and is why the greedy Dijkstra's Algorithm cannot be trusted here.
PICTURE. Look at the four dots and their arrows. Green numbers are positive tolls; the magenta arrow has weight — a road that pays you.

- is the home dot (violet).
- Each arrow's label is its weight : the symbol is a function — feed it a road , it returns that road's number.
Step 2 — A distance array: our only memory
WHAT. We keep one list of numbers, one slot per vertex, called dist. The slot dist[v] holds the best route cost to that we know about so far — not necessarily the true answer yet, just our current best guess.
WHY. We refuse to be clever. We will only ever improve guesses, never finalize them early. That humility is the whole reason Bellman-Ford survives negative edges.
We start every guess as "impossibly far":
- : it costs nothing to already be home.
- (the sideways-8, "infinity") means "no route found yet". It is a placeholder for unreachable-so-far, guaranteed to be beaten by any real number.
PICTURE. The array as four boxes. Home is ; everyone else is .

Step 3 — One move: relaxation
WHAT. Pick any road with weight . Ask one question:
"If I travel to for
dist[u], then take this road for , is that cheaper than my current guessdist[v]?"
If yes, overwrite the guess. In code:
Term by term, at the exact spot each appears:
- — cost to reach the start of the road.
- — add the road's own toll.
- — compare against the current best guess at the destination.
- — the arrow means "store into"; we only store when it's an improvement.
WHY this operation and not another? Relaxation is the only way a route can get better: any cheaper route to must end on some final road , and its cost is exactly "(cost to ) + (that road)". So checking every road covers every possible improvement. This is the atom of Graph Relaxation.
PICTURE. Before: dist[2]=5. We relax with while dist[1]=4. Since , the guess drops to .

Step 4 — One pass: relax every road once
WHAT. A pass means: go through the entire list of edges and relax each one, in some fixed order.
WHY. A single relax only improves one destination. A whole pass gives every road one chance to hand its improvement forward. Doing this repeatedly lets good news travel across the graph like a wave.
PICTURE — the wave after pass 1. Start from all- (except home). Following the edge order :

- : , so
dist[1]=4. - : , so
dist[2]=5. - : ✅ — the negative road beats the direct one.
dist[2]=1. - : uses the fresh → , so
dist[3]=3.
Notice: because was relaxed before in this order, the improvement rippled two hops in a single pass. Order changes how far the wave travels per pass — but never the final answer.
Step 5 — Why the wave is a dynamic program in disguise
WHAT. Define a two-index table:
- = a budget on how many roads the route may use.
- = which vertex we're pricing.
The rule that builds row from row :
Reading it aloud: the cheapest route to within budget is either a route we already had within budget , or a within- route to some neighbour plus one last road . There is no third possibility — a route either uses that extra edge budget or it doesn't. This is the Dynamic Programming heart of the algorithm.
WHY it matches Step 4. Compare the term with the relaxation test . They are the same arithmetic. So one pass over all edges advances the budget by at least one. The dist[] array is just the current row of this table with the old rows thrown away (we never need them).
PICTURE. The table stacked as rows; each pass fills the next row downward; each cell points back to the min of two candidate arrows.

Step 6 — Why we stop at exactly passes
WHAT. We claim: after passes the guesses are the true answers, where = number of vertices.
WHY. Suppose there is no negative cycle. Then the true shortest route to any never revisits a vertex — because looping back would only add non-negative weight, so a repeat is never helpful. A route that visits each vertex at most once touches at most vertices, and vertices are joined by at most roads.
So every true shortest path fits inside a budget of edges — meaning row already contains the final answers:
PICTURE. A path threading all dots uses exactly arrows. Add a repeated dot and you've made a cycle, not a simple path.

Step 7 — The degenerate case: a negative cycle
WHAT. A negative cycle is a loop of roads whose weights sum to a negative number. Drive it once, you're richer; drive it forever, your "distance" falls without bound. There is then no finite shortest path to anything the loop can reach.
WHY one extra pass exposes it. After passes, everything that could settle has settled (Step 6). So run one more pass. If any edge can still be relaxed, the improvement cannot come from a longer simple path (those maxed out at edges) — the only remaining explanation is a cycle that keeps paying you. See Negative Weight Cycles.
PICTURE. The triangle with weights sums to . Each lap drops all three guesses by ; the numbers never converge.

Step 8 — Two full worked traces
Example A (normal, with a negative edge). Graph of Step 1, source :
| pass | dist[0] | dist[1] | dist[2] | dist[3] |
|---|---|---|---|---|
| init | 0 | ∞ | ∞ | ∞ |
| 1 | 0 | 4 | 1 | 3 |
| 2 | 0 | 4 | 1 | 3 |
Pass 2 changed nothing → the early-exit trick fires and we stop before passes. The extra detection pass also changes nothing → no negative cycle. Answer: .
Example B (negative cycle). Edges , source . Loop weight . After passes the distances are still dropping; the extra pass still relaxes an edge → we return a negative-cycle flag, not numbers (the numbers would be meaningless).
PICTURE. Both traces side by side: A converges and flattens; B keeps sliding downhill.

The one-picture summary
Everything above compressed into a single flow: init → relaxing passes (the wave) → one reacting pass (the detector).

Recall Feynman retelling — the whole walkthrough in plain words
You start at home with cost and mark every other city as "unknown" (). A relax is one tiny question about one road: "reach the start of this road, add its toll — cheaper than what I had at the end?" If yes, write the smaller number. A pass asks that question for every road once, so good news ripples one or more hops outward like a wave. Because a route that never repeats a city can use at most (cities ) roads, doing (cities ) full passes lets the wave reach as far as it ever needs to — the numbers are now final. Then you do one more pass just to listen: if any road still wants to lower a number, there's no honest reason left except a loop that pays you every lap — a negative cycle — so you raise a flag instead of reporting nonsense numbers. "V minus one to relax, one more to react."
Connections
- 3.5.11 Bellman-Ford algorithm — DP approach, negative cycles detection, O(VE) (Hinglish)
- Dijkstra's Algorithm — the greedy method the negative edges break
- Dynamic Programming — the table is the real engine
- Floyd-Warshall — all-pairs cousin, also negative-safe
- Shortest Path Problem
- Negative Weight Cycles
- Graph Relaxation
- SPFA — queue-based speedup of the same ripple