🧠
Top-Down DP with Memoization
💡 Memoization avoids recomputing overlapping subproblems by caching results, drastically improving efficiency while keeping the recursive structure.
Intuition
Use the same recursive approach but store results of dp[i][j] after computing them once, so repeated calls return cached results instantly.
Algorithm
- Initialize a 2D dp array with -1 to indicate uncomputed states.
- Define recursive function dfs(i, j) that returns dp[i][j] if computed.
- If not computed, compute minimal triangulation by trying all k between i and j.
- Store and return the computed dp[i][j].
💡 Memoization adds caching to recursion, which is conceptually simple but requires careful indexing.
Recurrence:dp[i][j] = min_{i<k<j} (dp[i][k] + dp[k][j] + values[i]*values[k]*values[j])
def minScoreTriangulation(values):
n = len(values)
dp = [[-1] * n for _ in range(n)]
def dfs(i, j):
if j <= i + 1:
return 0
if dp[i][j] != -1:
return dp[i][j]
res = float('inf')
for k in range(i + 1, j):
cost = dfs(i, k) + dfs(k, j) + values[i] * values[k] * values[j]
if cost < res:
res = cost
dp[i][j] = res
return res
return dfs(0, n - 1)
# Driver code
if __name__ == '__main__':
values = [1, 3, 1, 4, 1, 5]
print(minScoreTriangulation(values)) # Expected output: 13
Line Notes
dp = [[-1] * n for _ in range(n)]Initialize dp table with -1 to mark uncomputed states
if dp[i][j] != -1:Return cached result if already computed
dp[i][j] = resStore computed minimal triangulation cost for interval [i,j]
return dfs(0, n - 1)Start recursion with memoization for entire polygon
public class Solution {
private int[][] dp;
public int minScoreTriangulation(int[] values) {
int n = values.length;
dp = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
dp[i][j] = -1;
return dfs(values, 0, n - 1);
}
private int dfs(int[] values, int i, int j) {
if (j <= i + 1) return 0;
if (dp[i][j] != -1) return dp[i][j];
int res = Integer.MAX_VALUE;
for (int k = i + 1; k < j; k++) {
int cost = dfs(values, i, k) + dfs(values, k, j) + values[i] * values[k] * values[j];
if (cost < res) res = cost;
}
dp[i][j] = res;
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int[] values = {1, 3, 1, 4, 1, 5};
System.out.println(sol.minScoreTriangulation(values)); // Expected: 13
}
}
Line Notes
dp = new int[n][n];Create dp table to cache results
dp[i][j] = -1;Mark states as uncomputed
if (dp[i][j] != -1) return dp[i][j];Return cached result if available
dp[i][j] = res;Store computed minimal triangulation cost
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int dfs(const vector<int>& values, int i, int j, vector<vector<int>>& dp) {
if (j <= i + 1) return 0;
if (dp[i][j] != -1) return dp[i][j];
int res = INT_MAX;
for (int k = i + 1; k < j; ++k) {
int cost = dfs(values, i, k, dp) + dfs(values, k, j, dp) + values[i] * values[k] * values[j];
if (cost < res) res = cost;
}
dp[i][j] = res;
return res;
}
int minScoreTriangulation(vector<int>& values) {
int n = (int)values.size();
vector<vector<int>> dp(n, vector<int>(n, -1));
return dfs(values, 0, n - 1, dp);
}
int main() {
vector<int> values = {1, 3, 1, 4, 1, 5};
cout << minScoreTriangulation(values) << endl; // Expected: 13
return 0;
}
Line Notes
vector<vector<int>> dp(n, vector<int>(n, -1));Initialize dp table with -1 for memoization
if (dp[i][j] != -1) return dp[i][j];Return cached result if computed
dp[i][j] = res;Cache computed minimal triangulation cost
return dfs(values, 0, n - 1, dp);Start memoized recursion
function minScoreTriangulation(values) {
const n = values.length;
const dp = Array.from({ length: n }, () => Array(n).fill(-1));
function dfs(i, j) {
if (j <= i + 1) return 0;
if (dp[i][j] !== -1) return dp[i][j];
let res = Infinity;
for (let k = i + 1; k < j; k++) {
const cost = dfs(i, k) + dfs(k, j) + values[i] * values[k] * values[j];
if (cost < res) res = cost;
}
dp[i][j] = res;
return res;
}
return dfs(0, n - 1);
}
// Test
console.log(minScoreTriangulation([1, 3, 1, 4, 1, 5])); // Expected: 13
Line Notes
const dp = Array.from({ length: n }, () => Array(n).fill(-1));Create 2D dp array initialized to -1 for memoization
if (dp[i][j] !== -1) return dp[i][j];Return cached result if available
dp[i][j] = res;Store computed minimal triangulation cost
return dfs(0, n - 1);Start memoized recursion for entire polygon
TimeO(n^3)
SpaceO(n^2) for dp table + O(n) recursion stack
Memoization ensures each dp[i][j] is computed once, and each computation tries O(n) splits, totaling O(n^3).
💡 For n=50, this is about 125,000 operations, which is efficient enough for interviews.
Interview Verdict: Accepted
Memoization makes the solution efficient and practical for interview constraints.