DynamicProgramming Facebook Medium DailyCodingProblem TODO
Problem
A builder is looking to build a row of n houses that can be of k different colors. He has a goal of minimizing cost while ensuring that no two neighboring houses are of the same color.
Given an n by k matrix where the nth row and kth column represents the cost to build the nth house with kth color, return the minimum cost which achieves this goal.
Solution
Examplify the problem to see a pattern
-
Empty input
n = 0, k = any→ cost = 0 (no houses to paint)
-
Single house
n = 1, k = 3, e.g.[[5,2,7]]- answer = 2 (pick the cheapest color)
-
Multiple houses, one color
n = 3, k = 1, e.g.[[4],[3],[5]]- Impossible to avoid same‐color neighbors
- Invalid set
-
Small example
n = 3, k = 3
costs = [
[1, 5, 3],
[2, 9, 4],
[3, 6, 1]
]
- house0 → color0 (1)
- house1 → color2 (4)
- house2 → color0 or2 ? best is color2 (1)
- total = 1+4+1 = 6
- Larger random example
- Verify correctness on random small arrays by brute force.
Brute-Force (Exhaustive Search) - O(k^n) time - O(n) space
Try every possible sequence of colors (kⁿ possibilities), skip sequences where adjacent colors match, track minimum cost.
Space complexity is recursion depth
#include <bits/stdc++.h>
using namespace std;
int n, k;
vector<vector<int>> cost;
int best = INT_MAX;
// Recursive helper: paint house `i`, with last color `lastColor`
void dfs(int i, int lastColor, int accCost) {
if (i == n) {
best = min(best, accCost);
return;
}
for (int c = 0; c < k; ++c) {
if (c == lastColor) continue;
int newCost = accCost + cost[i][c];
if (newCost >= best) continue; // prune
dfs(i+1, c, newCost);
}
}
int minCostBrute() {
if (n == 0) return 0;
best = INT_MAX;
dfs(0, -1, 0);
return best;
}
int main() {
// Example usage
cost = {{1,5,3},{2,9,4},{3,6,1}};
n = cost.size(); k = cost[0].size();
cout << minCostBrute() << "\n"; // expect 6
return 0;
}2. Classic DP - O(n·k²) time - O(n*k) space
Idea. Let dp[i][c] = minimum cost to paint up to house i with color c at i.
Transition:
dp[i][c] = cost[i][c] + min_{c'≠c}( dp[i-1][c'] )
Compute row by row in O(k) per color by scanning previous row.
Can reduce to O(k) space rolling array
#include <bits/stdc++.h>
using namespace std;
int minCostDP(const vector<vector<int>>& cost) {
int n = cost.size();
if (n == 0) return 0;
int k = cost[0].size();
if (k == 0) return 0;
vector<int> prev(cost[0]); // dp for house 0
vector<int> curr(k);
for (int i = 1; i < n; ++i) {
for (int c = 0; c < k; ++c) {
// find min over all other colors
int best = INT_MAX;
for (int pc = 0; pc < k; ++pc) {
if (pc == c) continue;
best = min(best, prev[pc]);
}
curr[c] = cost[i][c] + best;
}
swap(prev, curr);
}
return *min_element(prev.begin(), prev.end());
}
int main() {
vector<vector<int>> cost = {{1,5,3},{2,9,4},{3,6,1}};
cout << minCostDP(cost) << "\n"; // 6
return 0;
}3. Optimized DP with Two Minimums O(n*k) time - O(k) space
For each row i−1, track:
min1, the smallestdp[i−1][c], and its coloridx1.min2, the second smallest value.
Then for each color c at house i:
- If
c != idx1, usemin1; else usemin2. dp[i][c] = cost[i][c] + (c!=idx1 ? min1 : min2).
This avoids the inner k-scan, yielding O(k) per row.
#include <bits/stdc++.h>
using namespace std;
int minCostOptimized(const vector<vector<int>>& cost) {
int n = cost.size();
if (n == 0) return 0;
int k = cost[0].size();
if (k == 0) return 0;
// dp for previous row
vector<int> dp(cost[0]);
for (int i = 1; i < n; ++i) {
// find smallest and second smallest in dp
int min1 = INT_MAX, min2 = INT_MAX, idx1 = -1;
for (int c = 0; c < k; ++c) {
if (dp[c] < min1) {
min2 = min1;
min1 = dp[c];
idx1 = c;
} else if (dp[c] < min2) {
min2 = dp[c];
}
}
vector<int> newDp(k);
for (int c = 0; c < k; ++c) {
int bestPrev = (c == idx1 ? min2 : min1);
newDp[c] = cost[i][c] + bestPrev;
}
dp.swap(newDp);
}
return *min_element(dp.begin(), dp.end());
}
int main() {
vector<vector<int>> cost = {{1,5,3},{2,9,4},{3,6,1}};
cout << minCostOptimized(cost) << "\n"; // 6
return 0;
}