PATTERN 3 — WEIGHTED PATHS & SEARCH ON THE ANSWER#
The machine: relax edges until no relaxation improves anything — or stop asking for the optimum and start asking whether a candidate is feasible.
3.1 Sub-variant map#
| Sub-variant | The one idea | |
|---|---|---|
| A | Dijkstra — the template | The first pop of a node is final, and only if weights are non-negative |
| B | Dijkstra on implicit grids | The graph is m × n cells; the relaxation is the parameter |
| C | Augmented-state shortest path | dist is indexed by the whole state, not just the node |
| D | 0-1 BFS | Weights in {0,1} — a deque replaces the heap and the log disappears |
| E | Bellman-Ford | Negative edges, or a cap on the number of edges used |
| F | Floyd–Warshall & all-pairs | n ≤ ~400, and the k loop is outermost |
| G | Binary search on the answer + connectivity | Feasibility is monotone in the threshold |
| H | Offline queries + DSU sweep | Sort the queries too, and answer them all in one pass |
| I | ⚠︎ Anti-patterns — when shortest path is the wrong machine | Each one runs the wrong algorithm until it visibly fails |
3.2 Problems#
ADijkstra, the template#
A heap of (dist, node), lazy deletion, skip stale pops. The relaxation operator is the only thing that varies.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 83★ | 743. Network Delay Time | Medium | A | The atom. if (d > dist[u]) continue; is the lazy deletion — omit it and it still works but degrades | |
| 84★ | 1514. Path with Maximum Probability | Medium | A | Max-heap, multiply instead of add. Dijkstra is still valid because products of values in [0,1] are monotone non-increasing — say why out loud | |
| 85★ | 1976. Number of Ways to Arrive at Destination | Medium | A | Counting paths during relaxation: < overwrites the count, == adds to it. The two branches are the problem | |
| 86○ | 2642. Design Graph With Shortest Path Calculator | Hard | A | Dijkstra as an API rather than a one-shot answer; edge additions between queries |
BDijkstra on implicit grids#
Same template, but neighbours are computed and dist is a 2-D array. The relaxation is often not a sum.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 87★⚠︎ | 1631. Path With Minimum Effort | Medium | B | Grid DP is the wrong instinct: 4-directional movement means there is no evaluation order in which every predecessor is already computed. Bottleneck relaxation: `max(dist[u], | |
| 88★ | 778. Swim in Rising Water | Hard | B | Same bottleneck shape, max(dist[u], grid[v]). Three valid solutions (Dijkstra, binary search + BFS, DSU by time) — know all three and when each is cleanest | |
| 89○ | 505. The Maze II PRO | Medium | B | The edge is a roll; its weight is the roll length. Free substitute: 1631 | |
| 90○ | 2577. Minimum Time to Visit a Cell In a Grid | Hard | B | The parity wait: if you arrive too early you bounce between two cells, so add 0 or 1 to fix parity |
CAugmented-state shortest path#
If two arrivals at the same node differ in anything that affects the future, they are different states.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 91★ | 1129. Shortest Path with Alternating Colors | Medium | C | The state is (node, lastColour). The layered-graph idea in its smallest form | |
| 92★ | 1786. Number of Restricted Paths From First to Last Node | Medium | C | Two machines composed: Dijkstra to get dist[], then memoised DAG DP over the strictly-decreasing edges | |
| 93○ | 1928. Minimum Cost to Reach Destination in Time | Hard | C | dist[node][timeUsed] — a genuinely 2-D distance table | |
| —↻ | 1293 / 864 (see §1.J) | C | The unweighted versions of the same idea. Solve those first |
D0-1 BFS#
Weights in {0,1}: push zero-cost neighbours to the front, cost-1 to the back. Correct for the same reason Dijkstra is, minus the heap.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 94★ | 1368. Minimum Cost to Make at Least One Valid Path in a Grid | Hard | D | The atom. Following the arrow costs 0, any other direction costs 1 | |
| 95★ | 2290. Minimum Obstacle Removal to Reach Corner | Hard | D | Empty cell 0, obstacle 1. The transfer test for 1368 — if it takes more than ten minutes, redo 1368 |
EBellman-Ford#
Relax every edge, V−1 times. Slower than Dijkstra and strictly more general: negative weights, and edge-count constraints.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 96★⚠︎ | 787. Cheapest Flights Within K Stops | Medium | E | Dijkstra is wrong here, not just awkward: with a stop budget the first pop of a node is no longer final — a cheap arrival with too many stops blocks an expensive arrival that could still finish. Bellman-Ford with k+1 rounds over a snapshot of the previous round's distances | |
| —↻ | 743. Network Delay Time | Medium | E | Re-solve #83 with Bellman-Ford; compare O(E log V) to O(V·E) on the given constraints |
FFloyd–Warshall & all-pairs#
Three nested loops with k outermost. n ≤ ~400 in the constraints is the tell.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 97★ | 1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance | Medium | F | The template, and the tie-break on the largest index | |
| 98★ | 399. Evaluate Division | Medium | F | Multiplicative relaxation on a weighted undirected graph. Solvable by DFS, Floyd, or weighted DSU — write at least two | |
| 99★ | 1462. Course Schedule IV | Medium | F | Transitive closure as boolean Floyd — reachability with the same three loops | |
| 100○ | 2101. Detonate the Maximum Bombs | Medium | F | Build a directed graph from geometry (range is not symmetric), then reachability from each node |
GBinary search on the answer + a connectivity check#
If feasible(x) is monotone, you never need the optimum — only a predicate. This is §3.G/H of Bundle 01 with BFS as the check.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 101★ | 2812. Find the Safest Path in a Grid | Medium | G | Multi-source BFS to compute the safety field, then binary search the threshold with a BFS feasibility check. Two machines, cleanly separated | |
| 102○ | 1102. Path With Maximum Minimum Value PRO | Medium | G | Maximin path. Free substitute: 778 | |
| —↻ | 1631. Path With Minimum Effort | Medium | G | Second solve: binary search the effort, BFS over edges within it. Write the mapping to the Dijkstra version explicitly | |
| —↻ | 778. Swim in Rising Water | Hard | G | Third solve: DSU adding cells in elevation order until 0 and n²−1 connect |
HOffline queries + DSU sweep#
Sorting the queries is allowed. Sort both, sweep once, and a per-query O(E) becomes one O(E α) pass.
| Solved | # | Problem | Diff | Sub-variant | Why it's essential |
|---|---|---|---|---|---|
| 103★ | 1697. Checking Existence of Edge Length Limited Paths | Hard | H | The canonical form: sort edges by weight, sort queries by limit, union forward, answer in original index order | |
| 104★ | 2503. Maximum Number of Points From Grid Queries | Hard | H | The same sweep with a heap instead of a sort for the edges — grid cells enter by value |
I⚠︎ Anti-patterns: when the shortest-path machine is wrong#
No new problems. Re-run each of these with the wrong algorithm first, watch it fail, then fix it. That failure is the lesson.
| Case | The wrong machine | Why it fails | Right machine |
|---|---|---|---|
| 787 (#96) | Dijkstra | Finality invariant is void once a stop budget exists | Bellman-Ford, k+1 snapshot rounds |
| 1631 (#87) | Grid DP | 4-directional movement admits no evaluation order | Dijkstra, or binary search + BFS |
| 1091 (#11) | DFS | DFS returns a path, never the shortest | BFS |
| Any negative edge | Dijkstra | A shorter route may be discovered after the node was finalised | Bellman-Ford; detect negative cycles with a V-th round |
| All-pairs at n = 2000 | Floyd–Warshall | O(n³) = 8×10⁹ | Dijkstra from each source, or reconsider the question |
| 128 (#67) | DSU | Correct, but heavier than the O(n) hash-set scan | Hash set, walking up only from sequence starts |
| MST for "shortest path between u and v" | Kruskal/Prim | The MST path between two nodes is frequently not the shortest path | Dijkstra |
3.3 Templates#
JavaA — Dijkstra. Lazy deletion; the first pop of a node is final.20 lines
// A — Dijkstra. Lazy deletion; the first pop of a node is final.
long[] dijkstra(int n, List<int[]>[] adj, int src) { // adj[u] = {v, w}
long[] dist = new long[n];
Arrays.fill(dist, Long.MAX_VALUE);
dist[src] = 0;
PriorityQueue<long[]> pq = new PriorityQueue<>(Comparator.comparingLong(a -> a[0]));
pq.add(new long[]{0, src});
while (!pq.isEmpty()) {
long[] top = pq.poll();
int u = (int) top[1];
if (top[0] > dist[u]) continue; // stale entry
for (int[] e : adj[u]) {
long nd = dist[u] + e[1]; // ← the relaxation is the parameter
if (nd < dist[e[0]]) { dist[e[0]] = nd; pq.add(new long[]{nd, e[0]}); }
}
}
return dist;
}
// Bottleneck variant (778, 1631, 1102): nd = Math.max(dist[u], w)
// Probability variant (1514): max-heap, nd = dist[u] * w, keep the largerJavaD — 0-1 BFS. Deque replaces the heap; zero-cost to the front, one-cost to the back.19 lines
// D — 0-1 BFS. Deque replaces the heap; zero-cost to the front, one-cost to the back.
int[] zeroOneBfs(int n, List<int[]>[] adj, int src) {
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
Deque<Integer> dq = new ArrayDeque<>();
dq.addFirst(src);
while (!dq.isEmpty()) {
int u = dq.pollFirst();
for (int[] e : adj[u]) {
int v = e[0], w = e[1]; // w is 0 or 1
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
if (w == 0) dq.addFirst(v); else dq.addLast(v);
}
}
}
return dist;
}JavaE — Bellman-Ford with at most k edges. The snapshot is mandatory.16 lines
// E — Bellman-Ford with at most k edges. The snapshot is mandatory.
int cheapest(int n, int[][] flights, int src, int dst, int k) {
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
for (int round = 0; round <= k; round++) {
int[] prev = dist.clone(); // ← without this, one round
for (int[] f : flights) { // can chain many edges
if (prev[f[0]] == Integer.MAX_VALUE) continue;
dist[f[1]] = Math.min(dist[f[1]], prev[f[0]] + f[2]);
}
}
return dist[dst] == Integer.MAX_VALUE ? -1 : dist[dst];
}
// Drop the k cap and run V-1 rounds for plain Bellman-Ford.
// A V-th round that still improves anything ⇒ a negative cycle.JavaF — Floyd-Warshall. k MUST be the outer loop.11 lines
// F — Floyd-Warshall. k MUST be the outer loop.
void floyd(int[][] d, int n) { // d[i][j] preloaded, INF elsewhere
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++) {
if (d[i][k] == INF) continue;
for (int j = 0; j < n; j++) {
if (d[k][j] == INF) continue;
d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
}
}
}JavaH — offline queries + DSU sweep.18 lines
// H — offline queries + DSU sweep.
boolean[] answer(int n, int[][] edges, int[][] queries) { // q = {u, v, limit}
Arrays.sort(edges, Comparator.comparingInt(e -> e[2]));
Integer[] order = new Integer[queries.length];
for (int i = 0; i < order.length; i++) order[i] = i;
Arrays.sort(order, Comparator.comparingInt(i -> queries[i][2]));
DSU dsu = new DSU(n);
boolean[] out = new boolean[queries.length];
int e = 0;
for (int qi : order) { // queries in limit order
while (e < edges.length && edges[e][2] < queries[qi][2]) {
dsu.union(edges[e][0], edges[e][1]);
e++;
}
out[qi] = dsu.find(queries[qi][0]) == dsu.find(queries[qi][1]);
}
return out; // answers in ORIGINAL order
}3.4 Failure modes#
| # | Failure | Symptom | Fix |
|---|---|---|---|
| 3.1 | int distance accumulator | Overflow on 1e5 edges of weight 1e4 | long for distances, always |
| 3.2 | No stale-entry skip in Dijkstra | Works, but the heap grows to O(E) and TLEs on dense inputs | if (d > dist[u]) continue; as the first line after the pop |
| 3.3 | visited[] used instead of comparing distances | A better route arriving later is discarded | Dijkstra needs no visited array; the nd < dist[v] test is the guard |
| 3.4 | Dijkstra on a graph with negative weights | Wrong answer, no error | Bellman-Ford |
| 3.5 | Bellman-Ford without the per-round snapshot | A single round chains multiple edges; the k cap is silently ignored | int[] prev = dist.clone() at the top of each round |
| 3.6 | Floyd with k in the inner loop | Wrong answers on paths of length ≥ 3 | k outermost. There is no exception |
| 3.7 | INF + w overflow in Floyd | Negative distances appear | Skip when either operand is INF, or use INF = 1e9 with long |
| 3.8 | 0-1 BFS with a general weight | Wrong answer, since the deque no longer holds a monotone frontier | Only weights in {0, 1}; anything else needs Dijkstra |
| 3.9 | feasible(x) not proved monotone before binary searching | Converges to garbage that passes small tests | Prove monotonicity on paper first — Bundle 01 §4.1 Step 2 |
| 3.10 | Offline sweep answering in sorted order | Answers correct but permuted | Keep the original query index and write back to out[qi] |
| 3.11 | Augmented state stored as dist[node] | Too-large answer or an infinite loop | Index by the full state tuple |