3.7.13 · D3Algorithm Paradigms

Worked examples — DP problems — matrix chain multiplication

2,947 words13 min readBack to topic

Before we start, recall the objects we keep re-using — including one, the split table, that we lean on so heavily it deserves its own definition.

Because two examples below fill these tables in code-order, here is the exact loop structure we follow — the variable names (, , , ) are used verbatim later, so meet them now:

n = len(p) - 1                     # number of matrices
m[i][i] = 0 for all i              # base case
for L in 2..n:                     # L = chain length, OUTERMOST
    for i in 1..n-L+1:             # left end of the interval
        j = i + L - 1              # right end (so length is exactly L)
        m[i][j] = +infinity
        for k in i..j-1:           # try every last-cut position
            cost = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]
            if cost < m[i][j]:
                m[i][j] = cost
                s[i][j] = k        # <-- remember the winning split

The scenario matrix

Every MCM input falls into one of these case classes. The last column names the worked example that covers it, so you can see the whole space is filled — starting from the truly empty chain.

Case class What makes it special Covered by
Empty chain: array length , no matrices at all Example 0
Degenerate: one matrix, nothing to multiply Example 1
Minimal branch: exactly one split, no choice Example 2
First real choice: two splits compete Example 3
All-square chain every equal → symmetry, ties Example 4
Skinny middle matrix one tiny dimension changes the answer Example 5
Tie between split points two give equal cost — which to store? Example 6
Scalar output () chain collapses to a number Example 7
Word problem (real world) translate a story into a array Example 8
Exam twist: count vs recover asked for the parenthesization, not just cost Example 9

We work them in order. Each example first asks you to Forecast the answer before you compute — that guess is where the learning happens.


Example 0 — Empty chain ()

Forecast: if there is nothing to multiply, is the cost , undefined, or an error? Guess before reading.

  1. Compute . Why this step? Every MCM run begins by turning the array into a matrix count. Here .
  2. Check whether any loop body executes. Why this step? The outer loop runs for L in 2..n, i.e. 2..0, which is empty — zero iterations. No interval with exists.
  3. Read off the answer. Why this step? There is no to return (there is no matrix ). The total cost of multiplying nothing is by convention, exactly as the empty sum is .

Verify: An empty product needs no work, so is the only sensible value; the loops never fire, so nothing breaks. This is the boundary you must handle in code (guard if n <= 1: return 0) — units are multiplications, and we performed none. ✓


Example 1 — Degenerate: a single matrix ()

Forecast: ...how much work is there if nothing gets multiplied? Guess before reading.

  1. Count the matrices. Why this step? The array has length , and . One matrix.
  2. Apply the base case. Why this step? The recurrence's first branch says . Here , so we hit the base case directly — there is no split point with because .

Verify: A single matrix is already "multiplied". No scalar multiplications happen, so is correct — units are multiplications, and we did none. ✓


Example 2 — Minimal branch: exactly one split ()

Forecast: with only two matrices there is nothing to choose — but what number comes out?

  1. List the valid splits. Why this step? ranges over , i.e. , so only . No minimum to take — one candidate, and so automatically.
  2. Plug into the combine formula. Why this step? Left block is , right block is , so combine cost is . Sub-costs .

Verify: The output is , so entries, each summing products → multiplications. Matches. ✓


Example 3 — The first real choice ()

This is the parent note's motivating chain — we redo it as a decision between the only two options, and . There are exactly two candidate trees, and the figure draws both so you can see which survives.

Forecast: one split puts the big dimension inside a combine, the other keeps it outside. Which is cheaper — and by how much?

Figure — DP problems — matrix chain multiplication
Figure — two binary trees side by side. Left (teal): the tree ; its root "×" node multiplies the leaf against the sub-tree , and its total cost is labelled . Right (plum): the tree ; here is the sub-tree and is the lone leaf, total cost . The burnt-orange arrow points from the label "cheaper split kept by min" to the plum tree's root — that is the branch the operator keeps, so . Read each tree bottom-up: leaves are original matrices, every "×" is one multiplication, and the tree's shape IS the parenthesization.

  1. Enumerate splits. Why this step? gives and . These are the only two ways to place the last multiplication (the two trees in the figure).
  2. Split : . Why this step? Left is (cost ), right is (cost ), combine .
  3. Split : . Why this step? Left is (cost ), right (cost ), combine .
  4. Take the min and record the split. Why this step? The recurrence keeps the cheaper candidate, and remembers which won. , so the winner is .

Verify: difference, matching the parent note's headline. In the figure, the plum tree (the one the orange arrow marks) is the survivor — its shape is exactly what encodes. ✓


Example 4 — All-square chain (symmetry & the ties it creates)

Forecast: when every matrix is identical in shape, does the split point matter at all?

  1. Every combine costs the same. Why this step? Each factor equals , so every combine is , regardless of .
  2. Every length-2 subchain costs . Why this step? A length-2 subchain has a single split, so its cost is just one combine ; this applies to both and , giving .
  3. Try both splits. Why this step? Symmetry predicts a tie, but the algorithm still enumerates all before trusting one.
    • :
    • :
  4. Result. Both give ; the min is (first-seen recorded, so ).

Verify: Any parenthesization does exactly two multiplies . The equal costs are the hallmark of a square chain — no order beats another. ✓


Example 5 — Skinny middle matrix (one tiny dimension rules)

Forecast: the (dimension ) is a bottleneck. Do good splits try to route through it?

We fill by chain length , exactly as the loop skeleton at the top demands.

Length 2 ():

Length 3 (): Why now? Each needs two length- answers, which we just computed. 4. 5.

Length 4 (, the answer): Why last? It depends on all shorter chains. 6.

Verify: The winning split puts the tiny into the outer combine (), which is the cheapest boundary. The "skinny" dimension acts like a discount coupon — good splits spend it on the biggest combine. ✓


Example 6 — A deliberate tie between split points

Forecast: the shape "goes up-down-up". Will the two splits land on the same cost?

  1. Length-2 costs. ; .
  2. Split : .
  3. Split : .
  4. Min. , so at ().

Now a true tie — change the last dimension so both splits cost the same:

Verify: In the tie variant, mirror symmetry ( reads — a palindrome) forces the two trees to cost the same. The algorithm's answer is optimal regardless of which it records. ✓


Example 7 — Scalar output ()

Forecast: the outer dimensions are . Does that make one split almost free?

  1. Length-2 costs. ; .
  2. Split : . .
  3. Split : . .
  4. Min. , so at ().

Verify: Because , both outer combines are tiny ( and ), so the winner is decided by the inner subchain. The final matrix is genuinely — a plain number — yet the intermediate work still costs multiplications. Degenerate outer shape ≠ free chain. ✓


Example 8 — Word problem (translate the story)

Forecast: should you fold the three tiny -ish transforms together first, then hit the big -row data — or march left to right?

  1. Build the array. Why this step? MCM only sees dimensions. Chain , , , . So
  2. Length-2: ; ; .
  3. Length-3:
  4. Length-4:

Result: means the last multiply is : fold the three transforms together first (they are cheap -ish), then apply the combined matrix to the huge -row data once.

Verify: The naive left-to-right order costs (the path); the optimal nearly halves the work — exactly the "combine the small stuff before touching the big matrix" intuition. ✓


Example 9 — Exam twist: recover the parenthesization, not just the cost

Forecast: you know the cost; the trick is turning split indices into nested brackets. Where does the first bracket close?

Recall from the definition box: is the cut point where interval made its cheapest last multiplication. To rebuild the brackets you read top-down, biggest interval first.

  1. Start at the whole chain . Why this step? Reconstruction always begins with the outermost interval. → last cut after : .
  2. Recurse on the left . Why this step? Its own last cut is .
  3. Recurse on . , i.e. just with the only possible split.
  4. Right side is a single matrix — no bracket.
  5. Assemble inside-out. Why this step? Substitute the pieces:

Verify: This matches the parent note's stated optimal parenthesization exactly, and its cost is . The table is read top-down (biggest interval first), then each sub-interval recursively. ✓


Recall Self-test: name the case class for each

(array length , i.e. zero matrices) vs vs . ::: empty chain, → cost , no loop iterations (Example 0). ::: all-square chain → every split ties (Example 4 pattern). ::: scalar output, palindrome dims → symmetric tie, final result is (Examples 6 & 7 combined). Which loop variable must be outermost so no subchain is unfilled? ::: the chain-length (grow from to ), never loop by row . What does store, and why keep it separate from ? ::: the winning cut for interval ; tells how much, tells how, so we can rebuild the brackets.


Connections

  • 3.7.13 DP problems — matrix chain multiplication (Hinglish) — the parent recurrence these examples exercise
  • Dynamic Programming — optimal substructure is why per-interval answers combine
  • Catalan Numbers — how many parenthesizations each of these chains has
  • Optimal Binary Search Tree — same "try every split, store " reconstruction
  • Burst Balloons — interval DP where the last element (not split) is fixed
  • Memoization vs Tabulation — top-down would compute these same lazily
  • Time Complexity Analysis — every example above is fill work