Problem
You have two types of tiles: a 2 x 1 domino shape and a tromino shape. You may rotate these shapes.
Given an integer n, return the number of ways to tile an 2 x n board. Since the answer may be very large, return it modulo 10^9 + 7.
In a tiling, every square must be covered by a tile. Two tilings are different if and only if there are two 4-directionally adjacent cells on the board such that exactly one of the tilings has both squares occupied by a tile.
Examples
Example 1:
| |
Example 2:
| |
Solution
Method 1 - Naive recursion
We are given a 2 × n grid, and we need to find the number of ways to tile the grid using the following tiles:
So, in total, we have 6 different types of tiles:
T1andT2(from dominoes)T3,T4,T5, andT6(from trominoes)
Let’s see how we can approach this problem using brute-force approach. Since we are only given a 2xn grid, let our current position on the grid be denoted by an index i which denotes that we are currently at ith column of grid.
Filling the rows
To fill in the 2 rows, we need:
- The parameter
nrepresents the number of columns remaining in the grid that we need to tile.- Each tile placement reduces the number of remaining columns based on its dimensions (how much of the grid it covers).
- and the column state
Column states
Based on that we may have a column state:
0→ gap in row 1 only (▉▉▃)1→ gap in row 1 only (▉▉▀)2→ no gaps (▉▉▉)
At each column, we have the choice to choose any form of the above 6 tiles. However, the current choice will be limited by our previous choices.
Column state is 0
This means the top row has a gap at column i, while the bottom row is fully tiled up to column i.
From this configuration, let’s consider the valid tile placement cases:
- Case 1: Place an L-shaped tromino (T6) to fill the top-row gap:
- This tromino extends across two columns (
iandi+1). - It fills the top-row gap at column
iwhile leaving no gaps in columni+1(both rows now fully filled). - The grid state transitions to colState = 2 (no gaps), which refers to a fully tiled state.
- Now, 2 columns are properly fixed thanks to this, hence we have
n - 2columns to fill in afterwards
- This tromino extends across two columns (
- Case 2: Place a horizontal domino to fill the top-row gap at column
i:- This domino fills the gap in the top row.
- However, it introduces a gap in the bottom row in the next column (
i+1). - The grid state transitions to colState = 1, which indicates a bottom-row gap.
- This time, only 1 column is properly fixed, leaving the remaining
n - 1columns to be tiled.
Column state is 1
When colState == 1, the current configuration of the grid indicates:
- The bottom row has a gap at column
i. - The top row is fully tiled up to column
i.
At this point, we can place tiles to progress in two valid ways:
Case 1: Place an L-shaped tromino (T4 or mirrored T5) to fill the bottom-row gap
- This tromino spans two columns (
iandi+1). - It fills the bottom-row gap at column
iwhile leaving no gaps in columni+1(both rows now become fully tiled). - The grid state transitions to colState = 2, indicating no gaps remain, meaning both rows are fully tiled at this point.
- Since 2 columns are fully tiled, the recursive function reduces the problem by considering the remaining
n - 2columns.
- This tromino spans two columns (
Case 2: Place a horizontal domino to fill the bottom-row gap
- This domino spans one column and fills the bottom-row gap at column
i, but it introduces a gap in the top row at columni+1. - The grid state transitions to colState = 0, indicating a new gap in the top row at the next column.
- Since 1 column is tiled in this step, the recursive function reduces the problem by considering the remaining
n - 1columns.
- This domino spans one column and fills the bottom-row gap at column
Column state is 2
When colState == 2, both rows up to column i are fully tiled (there are no gaps in column i or earlier). From this fully tiled state, we consider all possible ways to progress while tiling the remaining grid.
Case 1: Place a vertical domino (
n - 1, 0)- A vertical domino spans 1 column (
column i), filling the top and bottom rows completely at that column. - However, this introduces a gap into the top row of the next column (
i + 1). - The grid state transitions to colState = 0 (gap introduced in the top row).
- Since a vertical domino covers 1 column, the recursive function progresses with
n - 1columns remaining.
- A vertical domino spans 1 column (
Case 2: Place a vertical domino (
n - 1, 1)- A vertical domino spans 1 column (
column i), filling the top and bottom rows completely at that column. - This introduces a gap into the bottom row of the next column (
i + 1). - The grid state transitions to colState = 1 (gap introduced in the bottom row).
- Since a vertical domino covers 1 column, the recursive function progresses with
n - 1columns remaining.
- A vertical domino spans 1 column (
Case 3: Place two horizontal dominoes (
n - 1, 2)- Two horizontal dominoes span 1 column each, covering both rows at column
i. - After placing two horizontal dominoes, both rows are fully tiled at this state.
- The grid state remains colState = 2 (fully tiled).
- Since two horizontal dominoes are used, the recursive function progresses with
n - 1columns remaining.
- Two horizontal dominoes span 1 column each, covering both rows at column
Case 4: Place a tromino to cover 2 × 3 (
n - 2, 2)- A tromino spans 2 columns, completely filling both rows at those columns.
- This does not introduce any gaps, leaving both rows fully tiled.
- The grid state remains colState = 2 (fully tiled).
- Since a tromino spans 2 columns, the recursive function progresses with
n - 2columns remaining.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(3^n). The naive approach involves exploring all possible tile combinations at each columnn, leading to 3 recursive branches (colStatevalues). The total number of recursive calls grows exponentially, resulting inO(3^n)due to the branching factor. - 🧺 Space complexity:
O(n). The space complexity arises from the recursion stack depth, which is proportional to the number of columns (n). Without memoisation, only the stack is used, making the space complexityO(n).
Method 2 - Top Down DP
Caching (or memoisation) is critical to avoid redundant calculations for overlapping subproblems. Without caching, similar states (e.g., (n, colState)) are recomputed, leading to inefficiency. Memoisation stores already computed results, reducing the time complexity from exponential to linear.
We use a hash map (in Java) or a dictionary (in Python) to store the results for each state (n, colState):
- Before performing recursive calls, we check if the result for a given state is already cached in the map/dictionary.
- If cached, directly return the stored result.
- Otherwise, calculate the result, store it in the map/dictionary, and return it.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n)- Using memoisation ensures every unique state
(n, colState)is calculated only once. - For
ncolumns with3possible states (colState = 0, 1, 2), the total number of unique states isO(3 * n). - Each state is computed in constant time (
O(1)), so the overall time complexity isO(n).
- Using memoisation ensures every unique state
- 🧺 Space complexity:
O(n).- The memoisation structure stores
3 * nstates, requiringO(n)space. - The recursion stack depth is at most
n, also contributingO(n)space. - Total space complexity:
O(n).
- The memoisation structure stores
Method 3 - Bottom up DP
The bottom-up DP approach avoids recursion by iteratively solving subproblems starting from smaller grids. The key idea is to represent the state of each column using three configurations:
- Column State Definitions:
- colState = 0 (
▉▉▀): Top row has a gap, bottom row is fully tiled. - colState = 1 (
▉▉▃): Bottom row has a gap, top row is fully tiled. - colState = 2 (
▉▉▉): Both rows are fully tiled (no gaps).
- colState = 0 (
- Transition Logic:
- Compute the number of ways to reach each state (
colState = 0, 1, 2) for columnibased on previous columns (i-1andi-2). - Update the state using predefined transitions that depend on tile placements (domino and tromino tiles).
- Compute the number of ways to reach each state (
- Base Cases:
- colState = 2: Only 1 way to tile an empty grid.
- colState = 0 & colState = 1: Impossible to start with a gap in row 1 or row 2 for
n = 1.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n).- We compute the DP table for
ncolumns and evaluate 3 states (colState = 0, 1, 2) for each column. - Each state calculation takes constant time (
O(1)). - Overall time complexity:
O(n).
- We compute the DP table for
- 🧺 Space complexity:
O(n)- The DP table requires
O(n * 3)space since we store results for3states for each column. - Hence, space complexity is
O(n).
- The DP table requires