Problem
You are painting a fence of n
posts with k
different colors. You must paint the posts following these rules:
- Every post must be painted exactly one color.
- There cannot be three or more consecutive posts with the same color.
Given the two integers n
and k
, return the number of ways you can paint the fence.
Example 1:
1 | Input: n = 3, k = 2 |
Example 2:
1 | Input: n = 1, k = 1 |
Example 3:
1 | Input: n = 7, k = 2 |
Constraints:
- 1 <= n <= 50
- 1 <= k <= 105
- The testcases are generated such that the answer is in the range [0, 231 - 1] for the given
n
andk
.
Bottom-up Dynamic Programming
Intuition
Right after finish reading the question, I feel I should calculate subproblems of the final problem and the subproblems are overlapped. In addition, the questions asks for number of ways
.
These are all signals that indicate this is a dynamic programming question. In general, there are four steps to solve a dynamic programming question:
- Divide a complex problem into a number of simpler overlapping subproblems
- some would say this step is to define the optimal solution
- Define a recurrence relation to solve larger subproblems from smaller subproblems
- Store solutions to each of these subproblems, solving each subproblem only once
- Use stored solutions to solve the original problem
Algorithm
- Let 2D array
opt[i][j]
denote the optimal number of ways to paint posti
with colorj
- Initialize the array: for
opt[i][j]
wherei == 0
,opt[i][j] = 1
and wherei == 1
,opt[i][j] = k
- The recurrence relation for
i >= 2
isopt[i][j] = sigma(opt[i-1][!j]) + sigma(opt[i-2][!j])
- Iterate
i
from2
ton-1
, build and store the solutions for each subproblem - The solution to the original problem is
sigma(opt[n-1][j])
wherej = [0, k)
Implementation
1 | class Solution { |
Complexity Analysis
Time Complexity
O(n*k)
We iterate n
times and each time we calculate the sum of previous row, which is of size k
Space Complexity
O(n*k)
We created a 2D array to store the optimal solution for each subproblem.
Dynamic Programming (Time and Space Improvement)
Intuition
Though it can be accepted by the judge, the first solution is a little time & space consuming. Let’s examine the problem more carefully:
When painting the 1st post, we have k
ways.
When painting the 2nd post, we have k*k
ways, since we can have two consecutive posts colored with the same color.
When painting the 3rd post, we have to pay attention as the question doesn’t allow three consecutive posts colored with the same color. How do we achieve that?
The answer is simple: break the harmony! Let the last three posts not being able to form the same color. Here is how:
Pick a color that is other than the color of the 2nd post.
This complies the constraint by the fact that at least the 3rd post and the 2nd post are not painted with the same color.
Here don’t waste your time thinking about what exact color the 2nd post was painted with, otherwise you’ll be swallowed by the black hole (just kidding). Just imagine that the 2nd post picked one color, then you have k-1
options now. Hence you can paint in k*k * (k-1)
ways.
You may ask how about the colors of the 2nd and the 1st post in this case, will they be painted with the same color? May be the same maybe not, but we don’t care! It doesn’t break any rule set by the question.
Pick a color that is other than the color of the 1st post.
This complies the constraint by the fact that at least the 3rd post and the 1st post are not painted with the same color.
Same here, just imagine that the 1st post picked one color, then you have k-1
colors to choose, hence there are 1 * (k-1)
ways to paint.
Hence, the optimal number of ways to paint the 3rd post is k*k * (k-1) + 1 * (k-1)
, i.e. (k*k + 1) * (k-1)
.
…
When painting the ith post, the number of ways is (ways(i-1) + ways(i-2)) * (k-1)
.
Great! It looks like we don’t need to know the exact color of a post at all in our solution.
Algorithm
- Let 1D array
opt[i]
denote the optimal number of ways to paint posts [0,i]- Initialzie the array:
opt[0] = k
andopt[1] = k*k
- Initialzie the array:
- The recurrence relation for
i >= 2
isopt[i] = (opt[i-1] + opt[i-2]) * (k-1)
- Iterate
i
from2
ton-1
, build and store the solutions for each subproblem - The solution to the original problem is
opt[n-1]
Implementation
1 | class Solution { |
Complexity Analysis
Time Complexity
O(n)
We traverse the array in one pass.
Space Complexity
O(n)
We create an auxiliary 1D array to store the optimal solution for each subproblem.
Dynamic Programming (Further Space Improvement)
Intuition
In the previous solution, for each subproblem opt[i]
we only need to keep opt[i-1]
and opt[i-2]
in order to calculate its value. We don’t need an array to store each state for this question.
Algorithm
- Let
p
be the optimal solution for previous post, andpp
be the one before previous post
- Initialzie the variables:
pp = k
andp = k*k
- The recurrence relation is the optimal number of ways for the current post is
(pp + p) * (k-1)
- Iterate
i
from2
ton-1
, caculate optimal solution for current subproblem and shift the values ofpp
andp
- The solution to the original problem is
p
Implementation
1 | class Solution { |
Complexity Analysis
Time Complexity
O(n)
We only have one for loop and in the worse case it has n-2
iterations.
Space Complexity
O(1)
We only create two variables to store the previous state.