Dynamic Programming · 12 of 60
Painting the Fence problem
Count the ways to paint a row of fence posts with k colors so that no three posts in a row share a color.
- Simple way
- time O(2^n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
A fence has n posts in a row and you have k paint colors. Paint every post. The one rule: you may not have three or more posts next to each other in the same color. Two in a row is fine.
Give back how many different ways there are to paint the fence. The answer gets huge, so most versions ask for it modulo 1,000,000,007.
A story to remember it
Picture it
Every way to paint 3 posts with 2 colors. Red red red and blue blue blue break the rule, which leaves 6 good paintings.
- answer
- rejected
Examples
Three posts, two colors
- In
n = 3, k = 2- Out
6
8 colorings in all, minus RRR and BBB.
Two posts
- In
n = 2, k = 4- Out
16
Any two colors are fine, even the same twice: 4 x 4.
Five posts, three colors
- In
n = 5, k = 3- Out
180
The walkthrough below builds this number post by post.
One color only
- In
n = 4, k = 1- Out
0
With one color, posts 1, 2 and 3 would all match. No good painting exists. With n = 2, k = 1 the answer is 1.
One post
- In
n = 1, k = 5- Out
5
Any color works.
Way 1: the simple way
Look at the last post. Call T(i) the number of good paintings of i posts.
- If the last post has a new color, the first
i - 1posts can be any good painting, and you havek - 1new colors. That gives(k - 1) x T(i - 1). - If the last post copies the one before, then those two form a pair, and that pair must be a switch from post
i - 2. That gives(k - 1) x T(i - 2).
So T(i) = (k - 1) x (T(i - 1) + T(i - 2)), with T(1) = k and T(2) = k x k. Write it as a plain recursive function.
The calls for T(5). T(3) is worked out twice and T(2) three times. The bigger n gets, the more repeats there are, just like Fibonacci.
- checking
class Solution {
static final long MOD = 1_000_000_007L;
public long countWays(int n, int k) {
if (n == 0) return 0;
if (n == 1) return k % MOD;
if (n == 2) return (long) k * k % MOD;
// new color after any painting of n-1, or a pair after any painting of n-2
return (k - 1) * ((countWays(n - 1, k) + countWays(n - 2, k)) % MOD) % MOD;
}
}How long it takes: each call makes two calls, so the work grows like Fibonacci numbers, about O(2^n). Extra room: the call chain, O(n).
Way 2: the clever way
Walk the posts from left to right and keep two counts.
same: paintings whose last two posts match.diff: paintings whose last post has a new color.- For the next post:
newSame = diff(copying is only safe after a switch) andnewDiff = (same + diff) x (k - 1)(switch after anything). - The total for the current post is
same + diff.
Five posts and three colors, one post at a time
01 / 09
Post 1 can take any of the 3 colors. There is no post before it, so we count it as a change: diff = 3, same = 0, total = 3.
- k
- 3
- looking now
- checking
- done
- answer
- just added
class Solution {
static final long MOD = 1_000_000_007L;
public long countWays(int n, int k) {
if (n == 0) return 0;
long same = 0; // last two posts match
long diff = k; // post 1: any color counts as a switch
for (int i = 2; i <= n; i++) {
long newSame = diff; // copy only after a switch
long newDiff = (same + diff) % MOD * (k - 1) % MOD; // switch after anything
same = newSame;
diff = newDiff;
}
return (same + diff) % MOD;
}
}How long it takes: one short step per post, O(n). Extra room: two numbers, O(1).
Watch out for
k = 1should give 1 for one or two posts and 0 for three or more. The formulas handle it becausek - 1 = 0.- Take the modulo after every multiply.
(same + diff) x (k - 1)can overflow alongwithout it. - Read the rule carefully. Some versions (LeetCode 276) say "no more than two adjacent posts", which is this problem. A stricter version says no two adjacent posts may match at all, and then the answer is
k x (k - 1)^(n - 1).
What interviewers ask next
- What if no three in a row means "at most
min a row"? Keepmcounts: paintings ending with a run of length 1, 2, ...,m. Each new post either extends the run or starts a new one withk - 1colors. - Can you do it in
O(log n)? Yes. The step from(same, diff)to the next pair is a fixed 2 by 2 multiply, so you can raise that 2 by 2 table to a power with repeated squaring. - Paint houses where each color has a price and no two neighbors match. That is the "paint house" problem: keep the cheapest cost ending in each color.
Remember this
Grown-up words: the two counts are DP states, T(i) = (k - 1)(T(i-1) + T(i-2)) is a recurrence relation, keeping only the last values is space optimization, and the repeated squaring trick is matrix exponentiation.