💡 Memoization avoids repeated calculations by caching results, making the recursive solution efficient and practical.
Intuition
Store results of subproblems so that each integer's maximum product is computed once and reused.
Algorithm
- Initialize a cache (dp array) to store maximum products for integers.
- If the result for n is cached, return it.
- Otherwise, compute max product by trying all cuts recursively.
- Store and return the computed result.
💡 Memoization transforms exponential recursion into polynomial time by avoiding duplicate work.
Recurrence:f(n) = max_{1 ≤ j < n} (max(j, f(j)) * max(n-j, f(n-j)))
def integer_break(n, dp=None):
if dp is None:
dp = [-1] * (n + 1)
if n == 1:
return 1
if dp[n] != -1:
return dp[n]
max_product = 0
for j in range(1, n):
left = max(j, integer_break(j, dp))
right = max(n - j, integer_break(n - j, dp))
max_product = max(max_product, left * right)
dp[n] = max_product
return max_product
# Driver code
if __name__ == '__main__':
print(integer_break(10)) # Expected output: 36
Line Notes
dp = [-1] * (n + 1)Initialize cache with -1 indicating uncomputed
if dp[n] != -1:Return cached result if available
dp[n] = max_productStore computed result to avoid recomputation
for j in range(1, n):Try all cuts to find max product
import java.util.Arrays;
public class IntegerBreak {
public static int integerBreak(int n) {
int[] dp = new int[n + 1];
Arrays.fill(dp, -1);
return helper(n, dp);
}
private static int helper(int n, int[] dp) {
if (n == 1) return 1;
if (dp[n] != -1) return dp[n];
int maxProduct = 0;
for (int j = 1; j < n; j++) {
int left = Math.max(j, helper(j, dp));
int right = Math.max(n - j, helper(n - j, dp));
maxProduct = Math.max(maxProduct, left * right);
}
dp[n] = maxProduct;
return maxProduct;
}
public static void main(String[] args) {
System.out.println(integerBreak(10)); // Expected output: 36
}
}
Line Notes
Arrays.fill(dp, -1);Mark all dp entries as uncomputed
if (dp[n] != -1) return dp[n];Return cached result if computed
dp[n] = maxProduct;Cache the computed max product
for (int j = 1; j < n; j++) {Try all possible cuts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int integerBreakHelper(int n, vector<int>& dp) {
if (n == 1) return 1;
if (dp[n] != -1) return dp[n];
int maxProduct = 0;
for (int j = 1; j < n; j++) {
int left = max(j, integerBreakHelper(j, dp));
int right = max(n - j, integerBreakHelper(n - j, dp));
maxProduct = max(maxProduct, left * right);
}
dp[n] = maxProduct;
return maxProduct;
}
int integerBreak(int n) {
vector<int> dp(n + 1, -1);
return integerBreakHelper(n, dp);
}
int main() {
cout << integerBreak(10) << endl; // Expected output: 36
return 0;
}
Line Notes
vector<int> dp(n + 1, -1);Initialize dp cache with -1
if (dp[n] != -1) return dp[n];Return cached result if available
dp[n] = maxProduct;Store computed result
for (int j = 1; j < n; j++) {Try all cuts to find max product
function integerBreak(n, dp = null) {
if (dp === null) dp = new Array(n + 1).fill(-1);
if (n === 1) return 1;
if (dp[n] !== -1) return dp[n];
let maxProduct = 0;
for (let j = 1; j < n; j++) {
let left = Math.max(j, integerBreak(j, dp));
let right = Math.max(n - j, integerBreak(n - j, dp));
maxProduct = Math.max(maxProduct, left * right);
}
dp[n] = maxProduct;
return maxProduct;
}
// Test
console.log(integerBreak(10)); // Expected output: 36
Line Notes
if (dp === null) dp = new Array(n + 1).fill(-1);Initialize memo array once
if (dp[n] !== -1) return dp[n];Return cached result if computed
dp[n] = maxProduct;Cache computed max product
for (let j = 1; j < n; j++) {Try all possible cuts
TimeO(n^2)
SpaceO(n) for dp array and recursion stack
Each integer up to n is computed once, and for each, we try all cuts up to n, resulting in quadratic time.
💡 For n=1000, this means about 1 million operations, which is efficient enough for interviews.
Interview Verdict: Accepted
Memoization makes the solution efficient and practical for large inputs.