The "Paint Fence" problem asks: Given n
fence posts and k
different colors, in how many ways can you paint all the posts such that no more than two adjacent fence posts have the same color?
Key constraints:
k
colors.n
and k
are positive integers (n ≥ 1, k ≥ 1).
At first glance, it might seem like we can simply choose any color for each post, yielding k^n
combinations. However, the constraint that no more than two adjacent posts can have the same color complicates things.
A brute-force approach would consider every possible painting and check if it meets the constraint, but this is highly inefficient for large n
or k
.
To optimize, we can use dynamic programming by recognizing that the color of the current post depends on the colors of the previous one or two posts. This allows us to build up the solution efficiently by keeping track of valid combinations as we paint each post.
We'll use dynamic programming to solve this problem efficiently. Let's define:
k
options.
k
colors for the first, and then use the same color again. So there are k
ways.k-1
other colors. So, k * (k-1)
ways.n
:
same
), then the previous two must have been different (diff
).
same = diff_prev * 1
diff
), then the previous post could have been painted in any valid way, and we choose any of the k-1
colors.
diff = (same_prev + diff_prev) * (k-1)
same + diff
after the last post.
We use just two variables to keep track of previous computations, optimizing space to O(1).
Example: n = 3
, k = 2
k = 2
ways (either color A or color B).
All possible combinations for 3 posts and 2 colors, ensuring no three adjacent are the same, are accounted for.
Brute-force approach:
The dynamic programming approach is much more efficient and scalable for large n
and k
.
The Paint Fence problem is a classic example of how dynamic programming can simplify combinatorial problems with constraints. By recognizing the relationship between the current and previous posts, we can avoid redundant calculations and achieve a linear-time solution. The key insight is to track scenarios where the last two posts are the same or different, enabling us to efficiently compute the total number of valid colorings. This approach is both elegant and highly efficient compared to brute-force enumeration.
def numWays(n, k):
if n == 0:
return 0
if n == 1:
return k
same = k * 1
diff = k * (k - 1)
for i in range(3, n + 1):
prev_diff = diff
diff = (same + diff) * (k - 1)
same = prev_diff * 1
return same + diff
class Solution {
public:
int numWays(int n, int k) {
if (n == 0) return 0;
if (n == 1) return k;
int same = k * 1;
int diff = k * (k - 1);
for (int i = 3; i <= n; ++i) {
int prev_diff = diff;
diff = (same + diff) * (k - 1);
same = prev_diff * 1;
}
return same + diff;
}
};
class Solution {
public int numWays(int n, int k) {
if (n == 0) return 0;
if (n == 1) return k;
int same = k * 1;
int diff = k * (k - 1);
for (int i = 3; i <= n; i++) {
int prev_diff = diff;
diff = (same + diff) * (k - 1);
same = prev_diff * 1;
}
return same + diff;
}
}
var numWays = function(n, k) {
if (n === 0) return 0;
if (n === 1) return k;
let same = k * 1;
let diff = k * (k - 1);
for (let i = 3; i <= n; i++) {
let prev_diff = diff;
diff = (same + diff) * (k - 1);
same = prev_diff * 1;
}
return same + diff;
};