0790-多米诺和托米诺平铺
有两种形状的瓷砖:一种是 2 x 1
的多米诺形,另一种是形如 “L” 的托米诺形。两种形状都可以旋转。
给定整数 n ,返回可以平铺 2 x n
的面板的方法的数量。 返回对 109 + 7
**取模 **的值。
平铺指的是每个正方形都必须有瓷砖覆盖。两个平铺不同,当且仅当面板上有四个方向上的相邻单元中的两个,使得恰好有一个平铺有一个瓷砖占据两个正方形。
示例 1:
**输入:** n = 3
**输出:** 5
**解释:** 五种不同的方法如上所示。
示例 2:
**输入:** n = 1
**输出:** 1
提示:
1 <= n <= 1000
方法一:动态规划
考虑这么一种平铺的方式:在第 i 列前面的正方形都被瓷砖覆盖,在第 i 列后面的正方形都没有被瓷砖覆盖(i 从 1 开始计数)。那么第 i 列的正方形有四种被覆盖的情况:
一个正方形都没有被覆盖,记为状态 0;
只有上方的正方形被覆盖,记为状态 1;
只有下方的正方形被覆盖,记为状态 2;
上下两个正方形都被覆盖,记为状态 3。
使用 dp}[i][s] 表示平铺到第 i 列时,各个状态 s 对应的平铺方法数量。考虑第 i-1 列和第 i 列正方形,它们之间的状态转移如下图(红色条表示新铺的瓷砖):
初始时 dp}[0][0] = 0, \textit{dp}[0][1] = 0, \textit{dp}[0][2] = 0, \textit{dp}[0][3] = 1,对应的状态转移方程(i \gt 0)为:
\begin{aligned}
\textit{dp}[i][0] &= \textit{dp}[i-1][3] \
\textit{dp}[i][1] &= \textit{dp}[i-1][0] + \textit{dp}[i-1][2] \
\textit{dp}[i][2] &= \textit{dp}[i-1][0] + \textit{dp}[i-1][1] \
\textit{dp}[i][3] &= \textit{dp}[i-1][0] + \textit{dp}[i-1][1] + \textit{dp}[i-1][2] + \textit{dp}[i-1][3] \
\end{aligned}
最后平铺到第 n 列时,上下两个正方形都被覆盖的状态 dp}[n][3] 对应的平铺方法数量就是总平铺方法数量。
1 | class Solution: |
1 | const long long mod = 1e9 + 7; |
1 | class Solution { |
1 | public class Solution { |
1 | var numTilings = function(n) { |
1 | const long long mod = 1e9 + 7; |
1 | func numTilings(n int) int { |
复杂度分析
时间复杂度:O(n),其中 n 是总列数。
空间复杂度:O(n)。保存 dp 数组需要 O(n) 的空间。
方法二:矩阵快速幂
关于矩阵快速幂的讲解可以参考官方题解「70. 爬楼梯 」的方法二,本文不再作详细说明。由方法一可知,平铺到某一列时的所有覆盖状态可以用一个列向量 x 来表示,那么初始时 x = [0 \ 0 \ 0 \ 1]^T。一次状态转移等价于在左边乘上矩阵:
A =
\begin{bmatrix}
0&0&0&1 \
1&0&1&0 \
1&1&0&0 \
1&1&1&1 \
\end{bmatrix}
那么 n 次状态转移后,所有覆盖状态对应的列向量为 A^n x,其中 A^n 可以使用矩阵快速幂来计算。根据 x 的值,可以知道最终所有的覆盖状态对应的列向量为 A^n 的第 3 列,返回该列向量的第 3 个元素即可。
1 | class Solution: |
1 | const long long mod = 1e9 + 7; |
1 | class Solution { |
1 | public class Solution { |
1 | const long long mod = 1e9 + 7; |
1 | const mod int = 1e9 + 7 |
复杂度分析
时间复杂度:O(\log n),其中 n 是总列数。矩阵快速幂的时间复杂度为 O(\log n)。
空间复杂度:O(1)。