🧠
Top-Down DP with Memoization
💡 Memoization avoids repeated calculations by caching results, making the recursive approach efficient.
Intuition
Store results of robFrom(i) so that each state is computed once, reducing exponential time to linear.
Algorithm
- Create a memo array initialized with -1 to store results.
- Define robFrom(i) that returns memo[i] if computed.
- If not computed, recursively compute robFrom(i+1) and robFrom(i+2) with choices.
- Store max result in memo[i] and return it.
💡 Memoization transforms exponential recursion into linear by caching.
Recurrence:dp[i] = max(dp[i+1], nums[i] + dp[i+2])
def rob(nums):
n = len(nums)
memo = [-1] * n
def robFrom(i):
if i >= n:
return 0
if memo[i] != -1:
return memo[i]
rob_current = nums[i] + robFrom(i + 2)
skip_current = robFrom(i + 1)
memo[i] = max(rob_current, skip_current)
return memo[i]
return robFrom(0)
# Driver code
if __name__ == '__main__':
print(rob([2,7,9,3,1])) # Expected output: 12
Line Notes
memo = [-1] * nInitialize memo array to mark uncomputed states
if memo[i] != -1:Return cached result if available
memo[i] = max(rob_current, skip_current)Cache computed result to avoid recomputation
return memo[i]Return the cached or newly computed max loot
import java.util.Arrays;
public class Solution {
private int[] memo;
public int rob(int[] nums) {
memo = new int[nums.length];
Arrays.fill(memo, -1);
return robFrom(nums, 0);
}
private int robFrom(int[] nums, int i) {
if (i >= nums.length) return 0;
if (memo[i] != -1) return memo[i];
int robCurrent = nums[i] + robFrom(nums, i + 2);
int skipCurrent = robFrom(nums, i + 1);
memo[i] = Math.max(robCurrent, skipCurrent);
return memo[i];
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.rob(new int[]{2,7,9,3,1})); // 12
}
}
Line Notes
memo = new int[nums.length]Create memo array to store results
Arrays.fill(memo, -1)Mark all states as uncomputed
if (memo[i] != -1) return memo[i];Return cached result if available
memo[i] = Math.max(robCurrent, skipCurrent);Cache computed max loot
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
vector<int> memo;
public:
int robFrom(const vector<int>& nums, int i) {
if (i >= nums.size()) return 0;
if (memo[i] != -1) return memo[i];
int robCurrent = nums[i] + robFrom(nums, i + 2);
int skipCurrent = robFrom(nums, i + 1);
memo[i] = max(robCurrent, skipCurrent);
return memo[i];
}
int rob(vector<int>& nums) {
memo.assign(nums.size(), -1);
return robFrom(nums, 0);
}
};
int main() {
Solution sol;
vector<int> nums = {2,7,9,3,1};
cout << sol.rob(nums) << endl; // 12
return 0;
}
Line Notes
memo.assign(nums.size(), -1);Initialize memo array with -1 for uncomputed states
if (memo[i] != -1) return memo[i];Return cached result if computed
memo[i] = max(robCurrent, skipCurrent);Cache the computed max loot
return memo[i];Return cached or computed result
function rob(nums) {
const n = nums.length;
const memo = new Array(n).fill(-1);
function robFrom(i) {
if (i >= n) return 0;
if (memo[i] !== -1) return memo[i];
const robCurrent = nums[i] + robFrom(i + 2);
const skipCurrent = robFrom(i + 1);
memo[i] = Math.max(robCurrent, skipCurrent);
return memo[i];
}
return robFrom(0);
}
// Test
console.log(rob([2,7,9,3,1])); // 12
Line Notes
const memo = new Array(n).fill(-1);Initialize memo array to mark uncomputed states
if (memo[i] !== -1) return memo[i];Return cached result if available
memo[i] = Math.max(robCurrent, skipCurrent);Cache computed max loot
return memo[i];Return cached or newly computed result
TimeO(n)
SpaceO(n) for memo and recursion stack
Each state is computed once and stored, reducing calls from exponential to linear.
💡 For n=20, this reduces calls from over a million to just 20.
Interview Verdict: Accepted
This approach is efficient enough for large inputs and is a common interview solution.