Skip to main content

Pattern 4: Interval DP Problems

The Range Merging Pattern: State is dp[i][j] = answer for range/substring [i...j]. Build from small intervals to large. Try all split points k between i and j.


The Core Pattern​

THE MENTAL MODEL

Imagine you have a rope with numbered segments:

  1. You can only combine adjacent segments
  2. Combining has a cost or value
  3. You want to find the optimal way to combine all segments
  4. You start with small ranges (2-3 segments) and build up to the full rope
  5. For each range, you try all possible ways to split it

This is Interval DP.

Pattern Recognition​

WHEN YOU SEE THIS PATTERN

Keywords: "substring", "subarray", "range [i...j]", "merge", "split", "palindrome", "optimal parenthesization"

Structure:

  • Operating on a contiguous range [i...j]
  • Answer for larger range depends on smaller sub-ranges
  • Need to try all ways to split the range

State: dp[i][j] = "best answer for range/substring from i to j"

Transition: Try all split points k between i and j:

dp[i][j] = best over all k of (dp[i][k] āŠ• dp[k+1][j] + merge_cost)

Build Order: By increasing length (length 1, 2, 3, ..., n)


Category 1: Core Interval DP Problems​

1. Longest Palindromic Substring (LC 5)​

PROBLEM

Given a string s, return the longest palindromic substring in s.

Example: s = "babad" → "bab" or "aba"

State Definition: dp[i][j] = true if substring s[i...j] is a palindrome

Transition:

dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i+1][j-1])
public String longestPalindrome(String s) {
int n = s.length();
if (n < 2) return s;

boolean[][] dp = new boolean[n][n];
int start = 0, maxLen = 1;

// Every single character is a palindrome
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;

if (s.charAt(i) == s.charAt(j)) {
// If length is 2 or inner substring is palindrome
if (len == 2 || dp[i+1][j-1]) {
dp[i][j] = true;
start = i;
maxLen = len;
}
}
}
}

return s.substring(start, start + maxLen);
}
THE FOUNDATION INSIGHT

Palindrome property: A string is a palindrome if:

  1. First and last characters match
  2. Inner substring is also a palindrome

This creates the interval dependency: dp[i][j] depends on dp[i+1][j-1]


2. Palindromic Substrings (LC 647)​

State Definition: dp[i][j] = true if s[i...j] is palindrome

Goal: Count all palindromic substrings

Transition: Same as Longest Palindromic Substring, but count instead of tracking max

public int countSubstrings(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
int count = 0;

// Single characters
for (int i = 0; i < n; i++) {
dp[i][i] = true;
count++;
}

// Build by length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;

if (s.charAt(i) == s.charAt(j)) {
if (len == 2 || dp[i+1][j-1]) {
dp[i][j] = true;
count++;
}
}
}
}

return count;
}
OPTIMIZATION VS COUNTING
  • Longest Palindromic Substring: Find the maximum length
  • Palindromic Substrings: Count all palindromes

Same structure, different goal!


3. Longest Palindromic Subsequence (LC 516)​

PROBLEM

Given a string s, find the longest palindromic subsequence's length.

A subsequence is a sequence that can be derived from another by deleting some or no elements without changing the order.

Example: s = "bbbab" → 4 (subsequence "bbbb")

State Definition: dp[i][j] = length of longest palindromic subsequence in s[i...j]

Transition:

if (s[i] == s[j]) {
dp[i][j] = 2 + dp[i+1][j-1]; // include both
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]); // skip one
}
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];

// Base case: single character
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;

if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = 2 + (len == 2 ? 0 : dp[i+1][j-1]);
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}

return dp[0][n-1];
}
SUBSTRING VS SUBSEQUENCE

Substring: Contiguous characters (must be adjacent)

  • dp[i][j] = is s[i...j] a palindrome? (boolean)

Subsequence: Non-contiguous (can skip characters)

  • dp[i][j] = longest palindrome length in s[i...j] (integer)

Key difference: Subsequence allows skipping characters!


4. Minimum Insertion Steps to Make String Palindrome (LC 1312)​

State Definition: dp[i][j] = minimum insertions needed to make s[i...j] a palindrome

Transition:

if (s[i] == s[j]) {
dp[i][j] = dp[i+1][j-1]; // no insertion needed
} else {
dp[i][j] = 1 + Math.min(dp[i+1][j], dp[i][j-1]); // insert one char
}
public int minInsertions(String s) {
int n = s.length();
int[][] dp = new int[n][n];

for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;

if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = (len == 2) ? 0 : dp[i+1][j-1];
} else {
dp[i][j] = 1 + Math.min(dp[i+1][j], dp[i][j-1]);
}
}
}

return dp[0][n-1];
}
RELATIONSHIP TO LONGEST PALINDROMIC SUBSEQUENCE
min_insertions = n - longest_palindromic_subsequence_length

Why? The characters NOT in the longest palindromic subsequence need to be inserted!


5. Burst Balloons (LC 312)​

PROBLEM

You have n balloons. Each balloon has a number on it. You are asked to burst all balloons.

If you burst balloon i, you get nums[i-1] * nums[i] * nums[i+1] coins.

Return the maximum coins you can collect.

State Definition: dp[i][j] = max coins from bursting balloons in range [i...j]

Transition: Try bursting each balloon k LAST in range [i...j]:

dp[i][j] = max over all k in [i,j] of:
dp[i][k-1] + dp[k+1][j] + nums[i-1] * nums[k] * nums[j+1]
public int maxCoins(int[] nums) {
int n = nums.length;
// Pad with 1s at both ends
int[] arr = new int[n + 2];
arr[0] = 1;
arr[n + 1] = 1;
for (int i = 0; i < n; i++) {
arr[i + 1] = nums[i];
}

int[][] dp = new int[n + 2][n + 2];

// Build by increasing length
for (int len = 1; len <= n; len++) {
for (int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;

// Try bursting each balloon k LAST in [i, j]
for (int k = i; k <= j; k++) {
int coins = arr[i-1] * arr[k] * arr[j+1];
coins += dp[i][k-1] + dp[k+1][j];
dp[i][j] = Math.max(dp[i][j], coins);
}
}
}

return dp[1][n];
}
WHY "BURST LAST"?

The paradigm shift: Instead of thinking "which to burst first", think "which to burst LAST".

When balloon k bursts last in [i, j]:

  • All other balloons in [i, j] are already gone
  • Its neighbors are arr[i-1] and arr[j+1] (the boundaries!)
  • This makes subproblems independent

See the Burst Balloons: Paradigm Shift document for full explanation!


Category 2: Matrix/Partition Problems​

6. Matrix Chain Multiplication (Classic)​

PROBLEM

Given dimensions of n matrices in an array where the ith matrix has dimensions p[i-1] Ɨ p[i], find the minimum number of scalar multiplications needed to multiply all matrices.

Example: Matrices A1 (10Ɨ20), A2 (20Ɨ30), A3 (30Ɨ40)

  • (A1 Ɨ A2) Ɨ A3 = 10Ɨ20Ɨ30 + 10Ɨ30Ɨ40 = 18,000
  • A1 Ɨ (A2 Ɨ A3) = 20Ɨ30Ɨ40 + 10Ɨ20Ɨ40 = 32,000

State Definition: dp[i][j] = minimum operations to multiply matrices from i to j

Transition: Try all split points k:

dp[i][j] = min over all k in [i, j-1] of:
dp[i][k] + dp[k+1][j] + p[i-1] * p[k] * p[j]
public int matrixChainMultiplication(int[] p) {
int n = p.length - 1; // number of matrices
int[][] dp = new int[n][n];

// Base case: single matrix needs 0 operations
// (already initialized to 0)

// Build by increasing chain length
for (int len = 2; len <= n; len++) {
for (int i = 0; i < n - len + 1; i++) {
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;

// Try all split points
for (int k = i; k < j; k++) {
int cost = dp[i][k] + dp[k+1][j] + p[i] * p[k+1] * p[j+1];
dp[i][j] = Math.min(dp[i][j], cost);
}
}
}

return dp[0][n-1];
}
THE CLASSIC INTERVAL DP

This is THE textbook example of interval DP:

  1. Range [i, j] = matrices from i to j
  2. Split at k = decide where to place parentheses
  3. Cost = operations for left + right + merging

Template for all partition problems!


7. Minimum Score Triangulation of Polygon (LC 1039)​

State Definition: dp[i][j] = minimum score to triangulate polygon vertices from i to j

Transition: Try all vertices k as the third vertex of triangle:

dp[i][j] = min over all k in [i+1, j-1] of:
dp[i][k] + dp[k][j] + values[i] * values[k] * values[j]
public int minScoreTriangulation(int[] values) {
int n = values.length;
int[][] dp = new int[n][n];

for (int len = 3; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;

// Try all vertices k as third point of triangle
for (int k = i + 1; k < j; k++) {
int score = dp[i][k] + dp[k][j] + values[i] * values[k] * values[j];
dp[i][j] = Math.min(dp[i][j], score);
}
}
}

return dp[0][n-1];
}
GEOMETRIC INTERPRETATION

Each triangulation divides the polygon into smaller polygons.

  • Edge [i, j] + vertex k forms a triangle
  • Recursively triangulate [i, k] and [k, j]

Same structure as Matrix Chain Multiplication!


8. Minimum Cost Tree From Leaf Values (LC 1130)​

State Definition: dp[i][j] = minimum sum of non-leaf nodes for subarray [i...j]

Transition: Split at k:

dp[i][j] = min over all k in [i, j-1] of:
dp[i][k] + dp[k+1][j] + max[i..k] * max[k+1..j]

9. Minimum Cost to Merge Stones (LC 1000)​

State Definition: dp[i][j][p] = minimum cost to merge stones [i...j] into p piles

Transition: 3D interval DP with pile count

3D INTERVAL DP

This extends interval DP with an extra dimension: number of piles remaining.

State: dp[i][j][piles]


10. Scramble String (LC 87)​

State Definition: dp[i][j][len] = true if s1[i..i+len] can scramble to s2[j..j+len]

Transition: Try all split points, and try both swap and no-swap

public boolean isScramble(String s1, String s2) {
if (s1.equals(s2)) return true;
if (s1.length() != s2.length()) return false;

int n = s1.length();
boolean[][][] dp = new boolean[n][n][n + 1];

// Base case: length 1
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j][1] = s1.charAt(i) == s2.charAt(j);
}
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
for (int j = 0; j <= n - len; j++) {
// Try all split points
for (int k = 1; k < len; k++) {
// No swap: s1[i..i+k] → s2[j..j+k], s1[i+k..i+len] → s2[j+k..j+len]
if (dp[i][j][k] && dp[i+k][j+k][len-k]) {
dp[i][j][len] = true;
break;
}

// Swap: s1[i..i+k] → s2[j+len-k..j+len], s1[i+k..i+len] → s2[j..j+len-k]
if (dp[i][j+len-k][k] && dp[i+k][j][len-k]) {
dp[i][j][len] = true;
break;
}
}
}
}
}

return dp[0][0][n];
}

Category 3: String Merging​

11. Remove Boxes (LC 546)​

State Definition: dp[i][j][k] = max points removing boxes [i...j] with k same-colored boxes adjacent after j

Transition: Try merging same-colored boxes at different positions

ADVANCED INTERVAL DP

This is one of the hardest interval DP problems.

3D State: [start][end][consecutive count]

Requires thinking about future merges with same colors.


12. Strange Printer (LC 664)​

State Definition: dp[i][j] = minimum turns to print substring s[i...j]

Transition: Try last printed position k:

dp[i][j] = min over all k in [i, j-1] where s[k] == s[j] of:
dp[i][k] + dp[k+1][j-1]
public int strangePrinter(String s) {
int n = s.length();
int[][] dp = new int[n][n];

// Base case: single character
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = len; // worst case: print each separately

// Try to find matching character to merge prints
for (int k = i; k < j; k++) {
int total = dp[i][k] + dp[k+1][j];
if (s.charAt(k) == s.charAt(j)) {
total--; // can merge the prints
}
dp[i][j] = Math.min(dp[i][j], total);
}
}
}

return dp[0][n-1];
}
MERGING STRATEGY

When s[k] == s[j], we can print them together in one operation.

This is about finding optimal groupings of same characters.


13. Minimum Cost to Cut a Stick (LC 1547)​

State Definition: dp[i][j] = minimum cost to make all cuts between positions i and j

Transition: Try cutting at each position k first:

dp[i][j] = min over all k in cuts between i and j of:
dp[i][k] + dp[k][j] + (stick[j] - stick[i])
public int minCost(int n, int[] cuts) {
int m = cuts.length;
int[] newCuts = new int[m + 2];
newCuts[0] = 0;
newCuts[m + 1] = n;
for (int i = 0; i < m; i++) {
newCuts[i + 1] = cuts[i];
}
Arrays.sort(newCuts);

int[][] dp = new int[m + 2][m + 2];

// Build by increasing gap
for (int len = 2; len < m + 2; len++) {
for (int i = 0; i + len < m + 2; i++) {
int j = i + len;
dp[i][j] = Integer.MAX_VALUE;

// Try all cuts between i and j
for (int k = i + 1; k < j; k++) {
dp[i][j] = Math.min(
dp[i][j],
dp[i][k] + dp[k][j] + newCuts[j] - newCuts[i]
);
}
}
}

return dp[0][m + 1];
}

14. Guess Number Higher or Lower II (LC 375)​

State Definition: dp[i][j] = minimum cost to guarantee a win in range [i...j]

Transition: Try guessing each number k:

dp[i][j] = min over all k in [i, j] of:
k + max(dp[i][k-1], dp[k+1][j])
public int getMoneyAmount(int n) {
int[][] dp = new int[n + 1][n + 1];

for (int len = 2; len <= n; len++) {
for (int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;

for (int k = i; k <= j; k++) {
int left = (k > i) ? dp[i][k-1] : 0;
int right = (k < j) ? dp[k+1][j] : 0;
dp[i][j] = Math.min(dp[i][j], k + Math.max(left, right));
}
}
}

return dp[1][n];
}
WORST-CASE OPTIMIZATION

We use max because we're preparing for the worst case (adversarial).

Then we minimize over all choices to find the best worst-case strategy.


15. Predict the Winner (LC 486)​

State Definition: dp[i][j] = maximum score difference (player1 - player2) in range [i...j]

Transition: Player can take from either end:

dp[i][j] = max(
nums[i] - dp[i+1][j], // take left
nums[j] - dp[i][j-1] // take right
)
public boolean PredictTheWinner(int[] nums) {
int n = nums.length;
int[][] dp = new int[n][n];

// Base case: single element
for (int i = 0; i < n; i++) {
dp[i][i] = nums[i];
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = Math.max(
nums[i] - dp[i+1][j], // take left
nums[j] - dp[i][j-1] // take right
);
}
}

return dp[0][n-1] >= 0;
}
GAME THEORY DP

State = score difference when playing optimally

Transition = max of (take item - opponent's best from remaining)

Negative sign because opponent's gain is your loss!


Category 4: Subsequence/Merging​

16. Stone Game VII (LC 1690)​

State Definition: dp[i][j] = maximum score difference in range [i...j]

Transition: Remove left or right stone, add sum of remaining:

dp[i][j] = max(
sum[i+1..j] - dp[i+1][j],
sum[i..j-1] - dp[i][j-1]
)

17. Palindrome Removal (LC 1246)​

State Definition: dp[i][j] = minimum moves to remove all elements in [i...j]

Transition: Merge same elements, or split at k


18. Minimum Number of Removals to Make Mountain Array (LC 1671)​

Approach: Find longest increasing subsequence from left and right

Interval-like: Build from both ends, find peak


19. Valid Parenthesis String (LC 678)​

State Definition: dp[i][j] = true if s[i...j] is valid with wildcards

Transition: Try matching pairs at different positions


20. Number of Ways to Divide a Long Corridor (LC 2147)​

State Definition: dp[i][j] = ways to partition [i...j] with seat constraints

Transition: Try placing dividers at valid positions


The Common Thread​

WHAT MAKES THESE ALL THE SAME PATTERN?

Every problem has:

  1. State: dp[i][j] = answer for subarray/substring from index i to j
  2. Build order: Solve by increasing interval length (len = 1, 2, 3, ..., n)
  3. Transition: Try all split points k where i ≤ k < j
  4. Recurrence pattern:
    dp[i][j] = best over all k of (dp[i][k] āŠ• dp[k+1][j] + merge_cost)
  5. Goal: Combine solutions of smaller intervals to solve larger intervals

Key Insight: You can't solve dp[i][j] until you've solved all smaller intervals it depends on. This creates a dependency structure that requires careful iteration order.


Classic Loop Structures​

Option 1: Build by Increasing Length​

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;

// Try all split points
for (int k = i; k < j; k++) {
dp[i][j] = best(
dp[i][j],
dp[i][k] + dp[k+1][j] + merge_cost(i, k, j)
);
}
}
}

Option 2: Iterate Backwards on i, Forwards on j​

for (int i = n-1; i >= 0; i--) {
for (int j = i; j < n; j++) {
// transitions using dp[i+1][...] and dp[...][j-1]
if (s[i] == s[j]) {
dp[i][j] = 2 + dp[i+1][j-1];
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
WHICH LOOP ORDER?

Length-based (Option 1):

  • More intuitive
  • Guarantees all dependencies computed
  • Works for all interval DP

Backwards-forwards (Option 2):

  • More compact
  • Works when dp[i][j] only depends on dp[i+1][...] and dp[...][j-1]
  • Common in palindrome problems

Both are correct if dependencies are respected!


Pattern Recognition Checklist​

USE THIS CHECKLIST

When you see a new problem, ask:

  • Does it involve a range/substring [i...j]?
  • Does the answer for a range depend on smaller sub-ranges?
  • Do I need to try all ways to split or merge the range?
  • Can I define dp[i][j] as "best answer for range i to j"?
  • Do smaller ranges need to be solved before larger ones?

If all YES → Pattern 4: Interval DP


Common Mistakes​

COMMON PITFALLS

Mistake 1: Wrong iteration order​

// WRONG - may use uncomputed values
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = dp[i+1][j-1]; // i+1 not computed yet!
}
}

// RIGHT - ensure dependencies computed first
for (int len = 1; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
// now dp[i+1][j-1] is already computed
}
}

Mistake 2: Boundary conditions​

// WRONG - accessing out of bounds
dp[i][j] = dp[i-1][j-1]; // what if i=0 or j=0?

// RIGHT - check boundaries
if (i > 0 && j > 0) {
dp[i][j] = dp[i-1][j-1];
}

Mistake 3: Base case initialization​

// Palindrome: single characters
for (int i = 0; i < n; i++) {
dp[i][i] = true; // or 1, depending on problem
}

// Matrix chain: single matrix
// dp[i][i] = 0 (no multiplication needed)

Space Optimization​

INTERVAL DP IS HARD TO OPTIMIZE

Unlike linear or grid DP, interval DP is difficult to space-optimize because:

  1. dp[i][j] depends on dp[i][k] and dp[k+1][j] for various k
  2. You need to access many positions in the 2D table
  3. Rolling array technique doesn't work well

Usually keep O(n²) space for interval DP.

Exception: Some palindrome problems can be optimized to O(n) with expand-from-center approach instead of DP.


Learning Path​

RECOMMENDED ORDER

Week 1: Palindromes (Problems 1-4)

  • Master palindrome interval DP
  • Learn length-based iteration
  • Understand substring vs subsequence

Week 2: Partitioning (Problems 5-10)

  • Matrix Chain Multiplication template
  • Burst Balloons paradigm shift
  • Triangulation and merging

Week 3: String Merging (Problems 11-15)

  • Advanced 3D interval DP
  • Game theory applications
  • Worst-case optimization

Week 4: Advanced (Problems 16-20)

  • Stone games
  • Complex constraints
  • Multi-dimensional intervals

Total: 20 problems Ɨ 4 weeks = Pattern 4 mastery


Quick Reference Table​

ProblemStateSplit/MergeOperationPattern Type
Longest Palindromeis [i,j] palindromematch endscheck charsBoolean
Palindrome Subseqmax length in [i,j]match/skipmaxOptimization
Burst Balloonsmax coins in [i,j]burst k lastmax + costPartition
Matrix Chainmin ops for [i,j]split at kmin + costClassic
Strange Printermin turns for [i,j]merge sameminMerging
Predict Winnerscore diff in [i,j]take left/rightmax(gain - opp)Game Theory
Min Cost Cutmin cost cuts [i,j]cut at kmin + lengthPartition

Dependency Visualization​

UNDERSTANDING DEPENDENCIES

For dp[i][j], typical dependencies:

Palindrome pattern:

dp[i][j] depends on:
- dp[i+1][j-1] (inner substring)

Partition pattern:

dp[i][j] depends on:
- dp[i][k] and dp[k+1][j] for all k in [i, j]

Visualization (for 4x4 table):

    0   1   2   3
0 [X] [1] [2] [3]
1 [X] [1] [2]
2 [X] [1]
3 [X]

X = base case
1 = computed in round 1 (length 2)
2 = computed in round 2 (length 3)
3 = computed in round 3 (length 4)

Diagonal filling: bottom-left to top-right, by diagonal


Next Steps​

AFTER MASTERING PATTERN 4

You're ready for:

  • Pattern 5: State Machine DP (mode transitions)
  • Advanced DP: Multi-dimensional intervals
  • DP on Trees (hierarchical intervals)

Connection to Previous Patterns:

  • Pattern 1: 1D state (single position)
  • Pattern 2: 2D state (two independent positions)
  • Pattern 3: 2D state (items + resource)
  • Pattern 4: 2D state (range endpoints) ← Dependent positions!

Final Thoughts​

INTERVAL DP IS THE MERGING PATTERN

Interval DP teaches you to think about ranges and merging.

Master these 20 problems, and you'll:

  • Recognize "try all split points" instantly
  • Know when to use length-based iteration
  • Handle palindromes, partitions, games naturally
  • Understand dependency structures in 2D DP

This is your range-based foundation. Build it strong.


Summary: The Interval DP Template​

// Template for Interval DP
public int intervalDP(int[] arr) {
int n = arr.length;
int[][] dp = new int[n][n];

// Base case: intervals of length 1
for (int i = 0; i < n; i++) {
dp[i][i] = baseValue(i);
}

// Build by increasing length
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = initValue; // MAX_VALUE for min, MIN_VALUE for max

// Try all split points
for (int k = i; k < j; k++) {
dp[i][j] = best(
dp[i][j],
combine(dp[i][k], dp[k+1][j], mergeCost(i, k, j))
);
}
}
}

return dp[0][n-1];
}
REMEMBER THE MANTRA

"For each range [i,j], try all ways to split it at k. The answer is the best combination of [i,k] and [k+1,j] plus the cost of merging."

This is the essence of Interval DP.