Matrix Chain Multiplication is the classic interval DP problem. You cannot change which matrices get multiplied, only the order you pair them up — and the right order can be hundreds of times cheaper than the wrong one.
Problem. You are given dimensions p of a chain of matrices, where matrix Ak has shape
p[k-1] x p[k]. Find the minimum number of scalar multiplications needed to compute the product
A1 · A2 · … · An. You may parenthesize the product any way you like; the result is the same, but the
cost differs.
Example: p = [40, 20, 30, 10, 30] describes 4 matrices. The cheapest order is (A1·(A2·A3))·A4,
costing 26000 multiplications.
The slow way first
Multiplying two matrices of shape a x b and b x c costs a · b · c scalar multiplications. For a
chain, the order of pairings forms a binary tree, and the number of valid parenthesizations grows like
the Catalan numbers — exponential. Trying them all is hopeless for anything but a tiny chain.
The question to ask: whatever the optimal order is, there must be one final multiplication that combines a left block and a right block. If I knew the best cost for every smaller block, I could just try each possible split point and take the cheapest. That is a textbook dynamic programming setup.
The idea: build up by chain length
Let dp[i][j] be the cheapest cost to multiply the sub-chain Ai … Aj. A single matrix needs no work,
so dp[i][i] = 0. For a longer chain we try every place k to make the final cut:
dp[i][j] = min over k of ( dp[i][k] + dp[k+1][j] + p[i-1]·p[k]·p[j] )
The first two terms are the costs of the left and right blocks (already solved, because they are shorter); the third is the cost of the one multiplication that joins them. We fill the table in order of increasing chain length, so every subproblem a cell needs is already computed.
The cell being filled depends only on cells to its left (same row) and below (same column) — all of which belong to shorter chains, hence already done.
Walk through it
Step through the animation. The grid is dp[i][j] (row i, column j); only the upper triangle is
used. The diagonal starts at 0. We fill length-2 chains, then length-3, then the full length-4 cell
dp[1][4]. Watch the final cell try all three splits k = 1, 2, 3 and keep the cheapest, 26000.
Pseudocode
n = number of matrices
dp[i][i] = 0 for every i # one matrix costs nothing
for length = 2 to n:
for i = 1 to n - length + 1:
j = i + length - 1
dp[i][j] = infinity
for k = i to j - 1: # try every split point
cost = dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j]
if cost < dp[i][j]:
dp[i][j] = cost # keep the cheapest split
return dp[1][n]The Python solution
def matrix_chain(p):
n = len(p) - 1
dp = [[0] * (n + 1) for _ in range(n + 1)]
for length in range(2, n + 1):
for i in range(1, n - length + 2):
j = i + length - 1
dp[i][j] = float('inf')
for k in range(i, j):
cost = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]
if cost < dp[i][j]:
dp[i][j] = cost
return dp[1][n]n = len(p) - 1is the number of matrices;phas one more entry than there are matrices.dpis(n+1) x (n+1), 1-indexed sodp[i][j]lines up with matricesAi … Aj. The diagonal stays0.- We loop by
lengthfirst so that when we computedp[i][j], the shorter blocks it reads are filled. j = i + length - 1is the right end of the current sub-chain.- The inner
kloop is the heart: every split point gives a candidate cost, and we keep the minimum. p[i - 1] * p[k] * p[j]is the cost of the single multiply that joins the left blockAi..Akto the right blockAk+1..Aj.- The answer for the whole chain is
dp[1][n].
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (all parenthesizations) | O(4^n / n^1.5) (moderate) | Catalan number of orders |
| Interval DP (this solution) | O(n^3) (moderate) | n^2 cells, each scans n splits |
O(n^2) (slow)There are O(n^2) subchains, and each scans up to n split points, giving O(n^3) time. The dp table
holds the O(n^2) answers.
When this pattern shows up
When a problem asks for the best way to combine a contiguous range — and the cost of a combination
depends on where you split it — reach for interval DP: a dp[i][j] table filled by increasing
range length, trying every split k inside. Burst Balloons, Boolean Parenthesization, optimal BST, and
stone-merging are all the same shape.
Fill the table by chain length, not by raw i/j order. If you loop i and j naively, you will
read dp[i][k] or dp[k+1][j] before it is computed and get garbage. The length-first loop guarantees
every dependency is already solved.
Practice
For p = [40, 20, 30, 10, 30], dp[1][4] tries splits k=1, k=2, k=3 with costs 36000, 69000, 26000. Which split wins, and what is the final answer?
1. What does dp[i][j] represent?
2. Why must we iterate by chain length before i and j?
3. What is the cost term for the single multiplication that joins the two blocks at split k?
4. What is the time complexity of the interval-DP solution?