💡 Memoization avoids recomputing the same states by caching results, drastically improving efficiency.
Intuition
Store results of subproblems defined by index and current sum to avoid repeated calculations.
Algorithm
- Initialize a memo dictionary keyed by (index, current sum).
- Recursively explore including or excluding current element.
- Before recursive calls, check if result is cached in memo.
- Return cached result if available, else compute and store it.
💡 Memoization transforms exponential recursion into polynomial by caching.
Recurrence:minDiff(i, currSum) = min(minDiff(i+1, currSum + arr[i]), minDiff(i+1, currSum)) with memoization
def min_subset_sum_diff(arr):
total_sum = sum(arr)
n = len(arr)
memo = {}
def helper(i, curr_sum):
if i == n:
return abs((total_sum - curr_sum) - curr_sum)
if (i, curr_sum) in memo:
return memo[(i, curr_sum)]
include = helper(i + 1, curr_sum + arr[i])
exclude = helper(i + 1, curr_sum)
memo[(i, curr_sum)] = min(include, exclude)
return memo[(i, curr_sum)]
# Driver code
if __name__ == '__main__':
arr = [1, 6, 11, 5]
print(min_subset_sum_diff(arr)) # Output: 1
Line Notes
memo = {}Initialize memo dictionary to cache results
if (i, curr_sum) in memo:Check if current state already computed
memo[(i, curr_sum)] = min(include, exclude)Store computed result for reuse
return memo[(i, curr_sum)]Return cached or newly computed result
import java.util.*;
public class MinSubsetSumDiff {
static Map<String, Integer> memo = new HashMap<>();
public static int minSubsetSumDiff(int[] arr) {
int totalSum = 0;
for (int num : arr) totalSum += num;
return helper(arr, 0, 0, totalSum);
}
private static int helper(int[] arr, int i, int currSum, int totalSum) {
if (i == arr.length) {
return Math.abs((totalSum - currSum) - currSum);
}
String key = i + "," + currSum;
if (memo.containsKey(key)) return memo.get(key);
int include = helper(arr, i + 1, currSum + arr[i], totalSum);
int exclude = helper(arr, i + 1, currSum, totalSum);
int res = Math.min(include, exclude);
memo.put(key, res);
return res;
}
public static void main(String[] args) {
int[] arr = {1, 6, 11, 5};
System.out.println(minSubsetSumDiff(arr)); // Output: 1
}
}
Line Notes
static Map<String, Integer> memo = new HashMap<>();Memo map to cache results keyed by index and sum
String key = i + "," + currSum;Create unique key for current state
if (memo.containsKey(key)) return memo.get(key);Return cached result if available
memo.put(key, res);Store computed result for future calls
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <cmath>
using namespace std;
unordered_map<string, int> memo;
string makeKey(int i, int currSum) {
return to_string(i) + "," + to_string(currSum);
}
int helper(const vector<int>& arr, int i, int currSum, int totalSum) {
if (i == arr.size()) {
return abs((totalSum - currSum) - currSum);
}
string key = makeKey(i, currSum);
if (memo.find(key) != memo.end()) return memo[key];
int include = helper(arr, i + 1, currSum + arr[i], totalSum);
int exclude = helper(arr, i + 1, currSum, totalSum);
memo[key] = min(include, exclude);
return memo[key];
}
int minSubsetSumDiff(const vector<int>& arr) {
int totalSum = 0;
for (int num : arr) totalSum += num;
memo.clear();
return helper(arr, 0, 0, totalSum);
}
int main() {
vector<int> arr = {1, 6, 11, 5};
cout << minSubsetSumDiff(arr) << endl; // Output: 1
return 0;
}
Line Notes
unordered_map<string, int> memo;Memo map to cache results
string key = makeKey(i, currSum);Create unique key for current state
if (memo.find(key) != memo.end()) return memo[key];Return cached result if found
memo[key] = min(include, exclude);Store computed result
function minSubsetSumDiff(arr) {
const totalSum = arr.reduce((a, b) => a + b, 0);
const n = arr.length;
const memo = new Map();
function helper(i, currSum) {
if (i === n) {
return Math.abs((totalSum - currSum) - currSum);
}
const key = i + ',' + currSum;
if (memo.has(key)) return memo.get(key);
const include = helper(i + 1, currSum + arr[i]);
const exclude = helper(i + 1, currSum);
const res = Math.min(include, exclude);
memo.set(key, res);
return res;
}
return helper(0, 0);
}
// Test
console.log(minSubsetSumDiff([1, 6, 11, 5])); // Output: 1
Line Notes
const memo = new Map();Initialize memo map for caching
const key = i + ',' + currSum;Create unique key for current state
if (memo.has(key)) return memo.get(key);Return cached result if present
memo.set(key, res);Store computed result for reuse
TimeO(n * total_sum)
SpaceO(n * total_sum) for memo
Memoization reduces exponential calls to polynomial by caching states defined by index and sum
💡 For n=100 and sum=1000, this is about 100,000 states, which is feasible.
Interview Verdict: Accepted
Memoization makes the solution efficient enough for typical constraints.