DEV Community

Cover image for Matrix Chain Multiplication: A New DP Pattern Built on One Question
Nishant Gaurav
Nishant Gaurav

Posted on

Matrix Chain Multiplication: A New DP Pattern Built on One Question

The Knapsack pattern asks: should I take this item or skip it? The LCS pattern asked: do these two characters match? The MCM pattern asks something different: where should I split this?

MCM is not one problem. It's a pattern that appears across eight or more problems: Matrix Chain Multiplication, Boolean Parenthesization, Palindrome Partitioning, Egg Dropping, and others. The problems look very different from each other. But once you see the shared structure, each new problem becomes a matter of changing one or two lines inside a familiar skeleton.

This chapter is about that skeleton.


How to Recognise an MCM Problem

When you see a new problem, look for these four signals:

  1. You're given an array or a string
  2. You need to partition or break it somehow
  3. You need a minimum, maximum, count, or boolean answer
  4. Multiple partition points are possible and you don't know which is optimal

The triggering question is: can I split this problem at some position k, solve the two halves independently, and combine the results?

If yes, MCM pattern likely applies.


The Core Idea

Every MCM problem works on a range [i, j]. You pick a partition point k somewhere inside that range, solve the left half [i, k] and the right half [k+1, j] independently, then combine the two results.

i ─────────────────── j

         ↓ split at k

i ──── k │ k+1 ──── j
Enter fullscreen mode Exit fullscreen mode

Since you don't know which k is optimal, you try every possible k in the range and keep the best result.


The Generic Structure

This pseudocode is the skeleton of every MCM problem. The only thing that varies between problems is what goes inside tempAns and whether you take min, max, count, or a boolean.

def solve(arr, i, j):

    # base case — smallest valid or first invalid input
    if i >= j:
        return 0

    ans = float('inf')   # or 0, or -inf, depending on the problem

    for k in range(i, j):

        left    = solve(arr, i, k)       # solve left partition
        right   = solve(arr, k + 1, j)   # solve right partition

        # combine results — THIS LINE changes per problem
        tempAns = left + right + cost(i, k, j)

        ans = min(ans, tempAns)           # or max, or +=, depending on the problem

    return ans
Enter fullscreen mode Exit fullscreen mode

The structure has three parts:

The boundaries i and j: Define which portion of the array you're currently solving. In the initial call, i = 0 and j = n-1 (or n depending on the problem).

The partition loop for k in range(i, j): Try every position where the range could be split. k goes from i to j-1, never reaching j because you need at least one element on each side.

The combination tempAns: This is where the problems differ. What does it cost or produce to split at k? How do the left and right results combine? Once you answer this for a specific problem, the rest of the skeleton is already written.

[


The Recursive Tree

Every call to solve(i, j) generates two calls for each value of k. Each of those calls generates more calls. The structure looks like this:

          solve(i, j)
               |
           k = i to j-1
            /      \
           /        \
    solve(i, k)   solve(k+1, j)
         |               |
         ...             ...
Enter fullscreen mode Exit fullscreen mode

The same subproblem — the same (i, j) pair — can appear through multiple paths. That's the overlapping subproblems signal from Chapter 1 of this series. When you add memoization or convert to tabulation, each (i, j) is computed only once.


Base Cases

Two questions to ask:

What is the smallest valid input? When i == j, only one element remains. This is often the base case. What should a single element return? Usually 0, because there's nothing to partition.

What is the first invalid input? When i > j, the pointers have crossed and there are no elements. This is always invalid. Return 0 or whatever the problem requires.

Which one you use depends on the problem. Some problems only need i == j. Others need to handle i > j too. But always ask both questions when designing the base case.

i ───────────── j        normal range
i === j                  one element (smallest valid)
i > j                    crossed pointers (invalid)
Enter fullscreen mode Exit fullscreen mode

What Changes Between MCM Problems

The skeleton is fixed. The tempAns line is the problem. Here's what varies:

Problem tempAns Aggregation
Matrix Chain Multiplication left + right + arr[i-1]*arr[k]*arr[j] min
Palindrome Partitioning left + right + 1 min
Boolean Parenthesization truth-table-based count +
Egg Dropping 1 + max(left, right) min
Min/Max expression value operator-based evaluation min and max

Same loop. Same recursive structure. Different tempAns. Different aggregation.


The Solving Flow for Any MCM Problem

When you encounter an MCM problem, go through these six steps in order:

  1. Identify the boundaries. What is i? What is j? What do they represent in this problem?
  2. Find the base case. When i == j, what should you return? When i > j, what should you return?
  3. Define the k range. Usually k goes from i to j-1.
  4. Write the recursive partition. solve(i, k) and solve(k+1, j).
  5. Calculate tempAns. This is the problem-specific step. What does splitting at k cost or produce?
  6. Apply aggregation. min, max, +, or boolean — depending on what the problem is asking for.

The next chapter applies this exact flow to the actual Matrix Chain Multiplication problem, where tempAns is the number of scalar multiplications needed to multiply the matrices from i to j with the split at k.


Quick Revision

Recognition signals:
  Array/string + partition + min/max/count/boolean + multiple split points

Core structure:
  solve(i, j)
    for k in range(i, j):
      left  = solve(i, k)
      right = solve(k+1, j)
      tempAns = combine(left, right)  ← changes per problem
      ans = min/max/count(ans, tempAns)
    return ans

Base cases:
  i == j → smallest valid input (usually return 0)
  i > j  → invalid (return 0)

What's fixed:     i, j boundaries + k loop + left/right recursion
What changes:     tempAns formula + aggregation type
Enter fullscreen mode Exit fullscreen mode

What You Now Understand

MCM is a pattern, not a problem. The structure — range [i, j], partition at every k, recurse on both halves, combine — is the same across all MCM problems. The only creative work is figuring out what tempAns means for the specific problem you're solving.

The next chapter applies this skeleton to Matrix Chain Multiplication specifically: given a chain of matrices, find the minimum number of scalar multiplications needed to compute their product. The tempAns line becomes concrete, and the pattern produces a working solution.

Top comments (0)