Welcome to Subscribe On Youtube
1411. Number of Ways to Paint N × 3 Grid
Description
You have a grid of size n x 3 and you want to paint each cell of the grid with exactly one of the three colors: Red, Yellow, or Green while making sure that no two adjacent cells have the same color (i.e., no two cells that share vertical or horizontal sides have the same color).
Given n the number of rows of the grid, return the number of ways you can paint this grid. As the answer may grow large, the answer must be computed modulo 109 + 7.
Example 1:

Input: n = 1 Output: 12 Explanation: There are 12 possible way to paint the grid as shown.
Example 2:
Input: n = 5000 Output: 30228214
Constraints:
n == grid.length1 <= n <= 5000
Solutions
Solution 1: Recursion
We classify all possible states for each row. According to the principle of symmetry, when a row only has $3$ elements, all legal states are classified as: $010$ type, $012$ type.
- When the state is $010$ type: The possible states for the next row are: $101$, $102$, $121$, $201$, $202$. These $5$ states can be summarized as $3$ $010$ types and $2$ $012$ types.
- When the state is $012$ type: The possible states for the next row are: $101$, $120$, $121$, $201$. These $4$ states can be summarized as $2$ $010$ types and $2$ $012$ types.
In summary, we can get: $newf0 = 3 \times f0 + 2 \times f1$, $newf1 = 2 \times f0 + 2 \times f1$.
The time complexity is $O(n)$, where $n$ is the number of rows in the grid. The space complexity is $O(1)$.
Solution 2: State Compression + Dynamic Programming
We notice that the grid only has $3$ columns, so there are at most $3^3=27$ different coloring schemes in a row.
Therefore, we define $f[i][j]$ to represent the number of schemes in the first $i$ rows, where the coloring state of the $i$th row is $j$. The state $f[i][j]$ is transferred from $f[i - 1][k]$, where $k$ is the coloring state of the $i - 1$th row, and $k$ and $j$ meet the requirement of different colors being adjacent. That is:
\[f[i][j] = \sum_{k \in \text{valid}(j)} f[i - 1][k]\]where $\text{valid}(j)$ represents all legal predecessor states of state $j$.
The final answer is the sum of $f[n][j]$, where $j$ is any legal state.
We notice that $f[i][j]$ is only related to $f[i - 1][k]$, so we can use a rolling array to optimize the space complexity.
The time complexity is $O((m + n) \times 3^{2m})$, and the space complexity is $O(3^m)$. Here, $m$ and $n$ are the number of rows and columns of the grid, respectively.
Solution 2: State Compression + Dynamic Programming
We notice that the grid only has $3$ columns, so there are at most $3^3=27$ different coloring schemes in a row.
Therefore, we define $f[i][j]$ to represent the number of schemes in the first $i$ rows, where the coloring state of the $i$th row is $j$. The state $f[i][j]$ is transferred from $f[i - 1][k]$, where $k$ is the coloring state of the $i - 1$th row, and $k$ and $j$ meet the requirement of different colors being adjacent. That is:
\[f[i][j] = \sum_{k \in \textit{valid}(j)} f[i - 1][k]\]where $\textit{valid}(j)$ represents all legal predecessor states of state $j$.
The final answer is the sum of $f[n][j]$, where $j$ is any legal state.
We notice that $f[i][j]$ is only related to $f[i - 1][k]$, so we can use a rolling array to optimize the space complexity.
The time complexity is $O((m + n) \times 3^{2m})$, and the space complexity is $O(3^m)$. Here, $m$ and $n$ are the number of rows and columns of the grid, respectively.
-
class Solution { public int numOfWays(int n) { int mod = (int) 1e9 + 7; long f0 = 6, f1 = 6; for (int i = 0; i < n - 1; ++i) { long g0 = (3 * f0 + 2 * f1) % mod; long g1 = (2 * f0 + 2 * f1) % mod; f0 = g0; f1 = g1; } return (int) (f0 + f1) % mod; } } // Solution 2 class Solution { public int numOfWays(int n) { final int mod = (int) 1e9 + 7; int m = 27; Set<Integer> valid = new HashSet<>(); int[] f = new int[m]; for (int i = 0; i < m; ++i) { if (f1(i)) { valid.add(i); f[i] = 1; } } Map<Integer, List<Integer>> d = new HashMap<>(); for (int i : valid) { for (int j : valid) { if (f2(i, j)) { d.computeIfAbsent(i, k -> new ArrayList<>()).add(j); } } } for (int k = 1; k < n; ++k) { int[] g = new int[m]; for (int i : valid) { for (int j : d.getOrDefault(i, List.of())) { g[j] = (g[j] + f[i]) % mod; } } f = g; } int ans = 0; for (int x : f) { ans = (ans + x) % mod; } return ans; } private boolean f1(int x) { int last = -1; for (int i = 0; i < 3; ++i) { if (x % 3 == last) { return false; } last = x % 3; x /= 3; } return true; } private boolean f2(int x, int y) { for (int i = 0; i < 3; ++i) { if (x % 3 == y % 3) { return false; } x /= 3; y /= 3; } return true; } } -
using ll = long long; class Solution { public: int numOfWays(int n) { int mod = 1e9 + 7; ll f0 = 6, f1 = 6; while (--n) { ll g0 = (f0 * 3 + f1 * 2) % mod; ll g1 = (f0 * 2 + f1 * 2) % mod; f0 = g0; f1 = g1; } return (int) (f0 + f1) % mod; } }; // Solution 2 class Solution { public: int numOfWays(int n) { int m = 27; auto f1 = [&](int x) { int last = -1; for (int i = 0; i < 3; ++i) { if (x % 3 == last) { return false; } last = x % 3; x /= 3; } return true; }; auto f2 = [&](int x, int y) { for (int i = 0; i < 3; ++i) { if (x % 3 == y % 3) { return false; } x /= 3; y /= 3; } return true; }; const int mod = 1e9 + 7; unordered_set<int> valid; vector<int> f(m); for (int i = 0; i < m; ++i) { if (f1(i)) { valid.insert(i); f[i] = 1; } } unordered_map<int, vector<int>> d; for (int i : valid) { for (int j : valid) { if (f2(i, j)) { d[i].push_back(j); } } } for (int k = 1; k < n; ++k) { vector<int> g(m); for (int i : valid) { for (int j : d[i]) { g[j] = (g[j] + f[i]) % mod; } } f = move(g); } int ans = 0; for (int x : f) { ans = (ans + x) % mod; } return ans; } }; -
class Solution: def numOfWays(self, n: int) -> int: mod = 10**9 + 7 f0 = f1 = 6 for _ in range(n - 1): g0 = (3 * f0 + 2 * f1) % mod g1 = (2 * f0 + 2 * f1) % mod f0, f1 = g0, g1 return (f0 + f1) % mod # Solution 2 class Solution: def numOfWays(self, n: int) -> int: def f1(x: int) -> bool: last = -1 for _ in range(3): if x % 3 == last: return False last = x % 3 x //= 3 return True def f2(x: int, y: int) -> bool: for _ in range(3): if x % 3 == y % 3: return False x //= 3 y //= 3 return True mod = 10**9 + 7 m = 27 valid = {i for i in range(m) if f1(i)} d = defaultdict(list) for i in valid: for j in valid: if f2(i, j): d[i].append(j) f = [int(i in valid) for i in range(m)] for _ in range(n - 1): g = [0] * m for i in valid: for j in d[i]: g[j] = (g[j] + f[i]) % mod f = g return sum(f) % mod -
func numOfWays(n int) int { mod := int(1e9) + 7 f0, f1 := 6, 6 for n > 1 { n-- g0 := (f0*3 + f1*2) % mod g1 := (f0*2 + f1*2) % mod f0, f1 = g0, g1 } return (f0 + f1) % mod } // Solution 2 func numOfWays(n int) (ans int) { f1 := func(x int) bool { last := -1 for i := 0; i < 3; i++ { if x%3 == last { return false } last = x % 3 x /= 3 } return true } f2 := func(x, y int) bool { for i := 0; i < 3; i++ { if x%3 == y%3 { return false } x /= 3 y /= 3 } return true } m := 27 valid := map[int]bool{} f := make([]int, m) for i := 0; i < m; i++ { if f1(i) { valid[i] = true f[i] = 1 } } d := map[int][]int{} for i := range valid { for j := range valid { if f2(i, j) { d[i] = append(d[i], j) } } } const mod int = 1e9 + 7 for k := 1; k < n; k++ { g := make([]int, m) for i := range valid { for _, j := range d[i] { g[i] = (g[i] + f[j]) % mod } } f = g } for _, x := range f { ans = (ans + x) % mod } return } -
function numOfWays(n: number): number { const mod: number = 10 ** 9 + 7; let f0: number = 6; let f1: number = 6; for (let i = 1; i < n; i++) { const g0: number = (3 * f0 + 2 * f1) % mod; const g1: number = (2 * f0 + 2 * f1) % mod; f0 = g0; f1 = g1; } return (f0 + f1) % mod; } // Solution 2 function numOfWays(n: number): number { const f1 = (x: number): boolean => { let last = -1; for (let i = 0; i < 3; ++i) { if (x % 3 === last) { return false; } last = x % 3; x = Math.floor(x / 3); } return true; }; const f2 = (x: number, y: number): boolean => { for (let i = 0; i < 3; ++i) { if (x % 3 === y % 3) { return false; } x = Math.floor(x / 3); y = Math.floor(y / 3); } return true; }; const m = 27; const valid = new Set<number>(); const f: number[] = Array(m).fill(0); for (let i = 0; i < m; ++i) { if (f1(i)) { valid.add(i); f[i] = 1; } } const d: Map<number, number[]> = new Map(); for (const i of valid) { for (const j of valid) { if (f2(i, j)) { d.set(i, (d.get(i) || []).concat(j)); } } } const mod = 10 ** 9 + 7; for (let k = 1; k < n; ++k) { const g: number[] = Array(m).fill(0); for (const i of valid) { for (const j of d.get(i) || []) { g[i] = (g[i] + f[j]) % mod; } } f.splice(0, f.length, ...g); } let ans = 0; for (const x of f) { ans = (ans + x) % mod; } return ans; } -
impl Solution { pub fn num_of_ways(n: i32) -> i32 { const MOD: i64 = 1_000_000_007; let mut f0: i64 = 6; let mut f1: i64 = 6; for _ in 0..n - 1 { let g0 = (3 * f0 + 2 * f1) % MOD; let g1 = (2 * f0 + 2 * f1) % MOD; f0 = g0; f1 = g1; } ((f0 + f1) % MOD) as i32 } } // Solution 2 use std::collections::{HashMap, HashSet}; impl Solution { pub fn num_of_ways(n: i32) -> i32 { const MOD: i32 = 1_000_000_007; let m = 27; let mut valid = HashSet::new(); let mut f = vec![0; m]; for i in 0..m { if Self::f1(i as i32) { valid.insert(i as i32); f[i] = 1; } } let mut d: HashMap<i32, Vec<i32>> = HashMap::new(); for &i in &valid { for &j in &valid { if Self::f2(i, j) { d.entry(i).or_insert_with(Vec::new).push(j); } } } for _ in 1..n { let mut g = vec![0; m]; for &i in &valid { if let Some(neighbors) = d.get(&i) { for &j in neighbors { g[j as usize] = (g[j as usize] + f[i as usize]) % MOD; } } } f = g; } let mut ans = 0; for x in f { ans = (ans + x) % MOD; } ans } fn f1(mut x: i32) -> bool { let mut last = -1; for _ in 0..3 { if x % 3 == last { return false; } last = x % 3; x /= 3; } true } fn f2(mut x: i32, mut y: i32) -> bool { for _ in 0..3 { if x % 3 == y % 3 { return false; } x /= 3; y /= 3; } true } }