🧠
Top-Down DP with Memoization
💡 Memoization caches results of subproblems to avoid redundant computations, drastically improving efficiency over brute force.
Intuition
Store the minimal cost for painting from house i with previous color c to avoid recomputing the same states multiple times.
Algorithm
- Initialize a memo dictionary to store results for (house, prev_color) states.
- Recursively compute minimal cost for each house and previous color, checking memo before recursion.
- For each color choice different from previous, compute cost plus recursive result.
- Return the minimal cost found and store it in memo.
💡 Memoization turns exponential recursion into polynomial time by caching overlapping subproblems.
Recurrence:f(i, c) = cost[i][c] + min_{x != c} f(i+1, x)
def minCost(costs):
n = len(costs)
k = len(costs[0]) if n > 0 else 0
memo = {}
def dfs(i, prev_color):
if i == n:
return 0
if (i, prev_color) in memo:
return memo[(i, prev_color)]
min_cost = float('inf')
for color in range(k):
if color != prev_color:
cost = costs[i][color] + dfs(i + 1, color)
if cost < min_cost:
min_cost = cost
memo[(i, prev_color)] = min_cost
return min_cost
return dfs(0, -1)
# Example usage
if __name__ == '__main__':
costs = [[1,5,3],[2,9,4],[15,7,6]]
print(minCost(costs))
Line Notes
memo = {}Initialize cache to store computed states
if (i, prev_color) in memo:Return cached result if available
memo[(i, prev_color)] = min_costStore computed minimal cost for state
return memo[(i, prev_color)]Return cached or computed result
import java.util.HashMap;
import java.util.Map;
public class PaintHouse {
private static Map<String, Integer> memo = new HashMap<>();
public static int minCost(int[][] costs) {
int n = costs.length;
int k = n > 0 ? costs[0].length : 0;
memo.clear();
return dfs(costs, 0, -1, n, k);
}
private static int dfs(int[][] costs, int i, int prevColor, int n, int k) {
if (i == n) return 0;
String key = i + "," + prevColor;
if (memo.containsKey(key)) return memo.get(key);
int minCost = Integer.MAX_VALUE;
for (int color = 0; color < k; color++) {
if (color != prevColor) {
int cost = costs[i][color] + dfs(costs, i + 1, color, n, k);
if (cost < minCost) minCost = cost;
}
}
memo.put(key, minCost);
return minCost;
}
public static void main(String[] args) {
int[][] costs = {{1,5,3},{2,9,4},{15,7,6}};
System.out.println(minCost(costs));
}
}
Line Notes
private static Map<String, Integer> memo = new HashMap<>();Cache to store computed states
String key = i + "," + prevColor;Create unique key for memoization
if (memo.containsKey(key)) return memo.get(key);Return cached result if exists
memo.put(key, minCost);Store computed minimal cost for state
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <climits>
using namespace std;
unordered_map<string, int> memo;
int dfs(const vector<vector<int>>& costs, int i, int prevColor, int n, int k) {
if (i == n) return 0;
string key = to_string(i) + "," + to_string(prevColor);
if (memo.find(key) != memo.end()) return memo[key];
int minCost = INT_MAX;
for (int color = 0; color < k; ++color) {
if (color != prevColor) {
int cost = costs[i][color] + dfs(costs, i + 1, color, n, k);
if (cost < minCost) minCost = cost;
}
}
memo[key] = minCost;
return minCost;
}
int minCost(vector<vector<int>>& costs) {
memo.clear();
int n = costs.size();
int k = n > 0 ? costs[0].size() : 0;
return dfs(costs, 0, -1, n, k);
}
int main() {
vector<vector<int>> costs = {{1,5,3},{2,9,4},{15,7,6}};
cout << minCost(costs) << endl;
return 0;
}
Line Notes
unordered_map<string, int> memo;Cache for memoization
string key = to_string(i) + "," + to_string(prevColor);Unique key for current state
if (memo.find(key) != memo.end()) return memo[key];Return cached result if available
memo[key] = minCost;Store computed minimal cost
function minCost(costs) {
const n = costs.length;
const k = n > 0 ? costs[0].length : 0;
const memo = new Map();
function dfs(i, prevColor) {
if (i === n) return 0;
const key = i + ',' + prevColor;
if (memo.has(key)) return memo.get(key);
let minCost = Infinity;
for (let color = 0; color < k; color++) {
if (color !== prevColor) {
const cost = costs[i][color] + dfs(i + 1, color);
if (cost < minCost) minCost = cost;
}
}
memo.set(key, minCost);
return minCost;
}
return dfs(0, -1);
}
// Example usage
const costs = [[1,5,3],[2,9,4],[15,7,6]];
console.log(minCost(costs));
Line Notes
const memo = new Map();Initialize cache for memoization
const key = i + ',' + prevColor;Create unique key for current state
if (memo.has(key)) return memo.get(key);Return cached result if exists
memo.set(key, minCost);Store computed minimal cost
For each house and previous color, we try k colors, leading to n*k*k states
💡 For n=1000 and k=10, this means about 100,000 operations, which is efficient.
Interview Verdict: Accepted
Memoization reduces redundant work, making the solution feasible for large inputs.