Dynamic Programming · Classic DP Patterns

Matrix Chain Multiplication

Trying every possible position to make the final split in a chain of matrix multiplications, recursively finding the optimal cost of each side, and combining them with the cost of that final multiplication - since the optimal split point isn't obvious ahead of time, every candidate split within a range must be tried and the cheapest kept.

  • 5 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: when a 1D prefix DP is enough
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

When you multiply a chain of matrices, the order of the multiplications doesn't change the result, but it hugely changes the work. For matrices of sizes 20x5, 5x50, 50x30 and 30x10, multiplying left to right costs 41,000 scalar multiplications and the best order costs 10,000. Ten matrices already have 4,862 possible orders. Interval DP finds the best one in O(n³). dp[i][j] is the cheapest way to combine items i through j, found by trying every split point k. The same idea, "solve every range by its best split", covers optimal binary search trees, bursting balloons and merging piles.

We'll go from simple to more technical:

  1. The pattern: dp[i][j] = min over k of dp[i][k] + dp[k + 1][j] + the cost of joining them, filled by length.
  2. A second flavor: when a 1D prefix DP is enough.
  3. Where interval DP shows up in production (query planning, expression evaluation).
  4. What breaks it: fill order, the join cost, greedy splits, overflow.

The approach

Java

int matrixChainMultiplication(int[] dims) {     // matrix i is dims[i - 1] x dims[i], for i = 1..n
    int n = dims.length - 1;
    int[][] dp = new int[n + 1][n + 1];         // dp[i][j] = cheapest cost to multiply matrices i..j
    for (int len = 2; len <= n; len++) {        // shorter ranges first
        for (int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            dp[i][j] = Integer.MAX_VALUE;
            for (int k = i; k < j; k++) {       // the LAST multiplication joins (i..k) and (k+1..j)
                int cost = dp[i][k] + dp[k + 1][j] + dims[i - 1] * dims[k] * dims[j];
                dp[i][j] = Math.min(dp[i][j], cost);
            }
        }
    }
    return dp[1][n];
}

Python

def matrix_chain_multiplication(dims: list[int]) -> int:   # matrix i is dims[i - 1] x dims[i], for i = 1..n
    n = len(dims) - 1
    dp = [[0] * (n + 1) for _ in range(n + 1)]   # dp[i][j] = cheapest cost to multiply matrices i..j
    for length in range(2, n + 1):               # shorter ranges first
        for i in range(1, n - length + 2):
            j = i + length - 1
            dp[i][j] = min(dp[i][k] + dp[k + 1][j] + dims[i - 1] * dims[k] * dims[j]
                           for k in range(i, j))  # the LAST multiplication joins (i..k) and (k+1..j)
    return dp[1][n]

Whatever the best order is, it ends with one final multiplication joining the product of matrices i..k with the product of k + 1..j. Those two results are dims[i - 1] x dims[k] and dims[k] x dims[j], so the final step costs dims[i - 1] * dims[k] * dims[j] - the outer dimensions of the two halves. Trying every k and filling ranges in increasing length (so both halves are always ready) is the interval-DP template.

A second flavor: when a 1D prefix DP is enough

Minimum cuts to split a string into palindromes looks like an interval problem - try every split of every range - but that's O(n^3), 8 * 10^9 steps for n = 2,000. The cuts only happen left to right, so a prefix DP suffices: cuts[i] = fewest cuts for the first i + 1 characters, trying every last palindrome s[j..i]:

Java

// isPal[j][i] precomputed in O(n^2); cuts[i] = min over j with isPal[j][i] of (j == 0 ? 0 : cuts[j - 1] + 1)

Python

# is_pal[j][i] precomputed in O(n^2); cuts[i] = min over j with is_pal[j][i] of (0 if j == 0 else cuts[j - 1] + 1)

That's O(n^2) - about 4 million steps. Interval DP is needed when the combining step depends on both halves as ranges (like the matrix dimensions); when pieces are simply laid end to end, a prefix DP is enough.

Where you'll see it

Database query optimizers choose join orders with the same idea (dynamic programming over subsets or ranges of tables, as in the classic System R optimizer), tensor libraries pick contraction orders for chains of matrix and tensor products (numpy.einsum_path), and compilers and parsers use interval DP (CYK parsing, optimal binary search trees built from access frequencies).

Which tool for which job?

You needUse
The cheapest way to combine a rangeinterval DP: best split k, filled by length
The order itselfa split[i][j] table, printed recursively
Pieces laid end to end (palindrome cuts)a 1D prefix DP, O(n²)
"Burst" or "remove" problemschoose the LAST item removed in each range
Cubic too slow, costs well-behavedKnuth's optimization, O(n²)
Easiest correct first versiontop-down memoization of best(i, j)

Common mistakes

MistakeWhat happensDo this instead
Filling ranges in the wrong orderreads unsolved sub-rangesby increasing length
The wrong join costwrong totalsdims[i - 1] × dims[k] × dims[j]
Cheapest adjacent pair first30,000 instead of 10,000try every split
int totals98 × 500³ wraps to -634901888long
Choosing the FIRST balloon to burstthe two halves aren't independentchoose the last one

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why does the final multiplication of the range i..j at split k cost dims[i - 1] * dims[k] * dims[j]?

Show answer

The left half multiplies out to a dims[i - 1] x dims[k] matrix and the right half to dims[k] x dims[j]. Multiplying a p x q matrix by a q x r one takes p * q * r scalar multiplications.

2. For dims [20, 5, 50, 30, 10], how do left-to-right, cheapest-pair-first and the DP compare?

Show answer

41,000, 30,000 and 10,000. Neither simple rule finds the optimum; the DP tries every final split of every range.

3. Why can palindrome min-cuts use an O(n^2) prefix DP while matrix chain needs O(n^3) interval DP?

Show answer

Palindrome pieces are just laid end to end, so the best answer for a prefix depends only on where its last piece starts - one index. Matrix chain's final step cost depends on both halves' outer dimensions, so every range and every split point matter.

Key takeaways

  • Interval DP: dp[i][j] for every range, built from sub-ranges, trying every split k.
  • Fill ranges by increasing length so both halves are ready.
  • For matrix chains, the final multiply costs the outer dimensions: dims[i - 1] * dims[k] * dims[j].
  • Greedy orders fail; there are Catalan-many parenthesizations, and DP handles them in O(n^3).
  • If pieces are only laid end to end, a prefix DP (O(n^2)) is enough.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Best way to combine a rangeinterval DP by length
The ordersplit[i][j]
End-to-end piecesprefix DP
Removal problemschoose the last item
Faster than cubicKnuth's window (when valid)

Snippets worth knowing by heart

Java

for (int len = 2; len <= n; len++)
    for (int i = 1; i + len - 1 <= n; i++) {
        int j = i + len - 1;
        cost[i][j] = Long.MAX_VALUE;
        for (int k = i; k < j; k++) {
            long c = cost[i][k] + cost[k + 1][j] + (long) d[i - 1] * d[k] * d[j];
            if (c < cost[i][j]) { cost[i][j] = c; split[i][j] = k; }
        }
    }

Python

@functools.cache
def best(l, r):                          # burst balloons strictly between l and r
    return max((best(l, k) + v[l] * v[k] * v[r] + best(k, r) for k in range(l + 1, r)), default=0)

The traps

  • Fill order: a cell reads sub-ranges that aren't solved yet. Fill ranges by increasing length.
  • The join cost: the totals come out wrong. Joining at k costs dims[i - 1] x dims[k] x dims[j].
  • Greedy splits: multiplying the cheapest adjacent pair first can cost three times more. Try every split.
  • int totals: large dimensions wrap negative. Use long.
  • Choosing the first item in removal problems: the two halves aren't independent. Choose the last one removed.

Staff-level points, one line each

  • Best order for 20×5, 5×50, 50×30, 30×10: (A1 ((A2 A3) A4)) at 10,000. Printed orders cost exactly the minimum on 200 chains.
  • Burst balloons with "last balloon" matched brute force on 200 arrays.
  • Optimal BST, 300 keys: 4,544,800 root tries plain, 89,663 with Knuth's window. Same cost.
  • Orders: 4,862 for 10 matrices, 1,767,263,190 for 20.
  • 98 × 500³ is 12,250,000,000 as a long and -634901888 as an int.

Say it in an interview

  • "dp[i][j] is the best for the range, trying every split, filled by increasing length."
  • "I keep the split points to print the order."
  • "For removals I pick the last one in the range, so the halves are independent."
  • "It's O(n³), or O(n²) with Knuth's optimization when the costs allow it."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Matrix Chain Multiplication free

Matrix Chain Multiplication

HardProCoding problemJava · Python

Given an array dims of length n + 1, where matrix i (for 1 <= i <= n) has dimensions dims[i-1] x dims[i], find the minimum number of scalar multiplications needed to compute the product of all n matrices in order (choosing the optimal way to parenthesize the multiplication).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Palindrome Partitioning (Minimum Cuts)

HardProCoding problemJava · Python

Cut s into pieces that are all palindromes, using as few cuts as you can, and return that number of cuts.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Matrix Chain Multiplication free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime