leetcode 790. Domino and Tromino Tiling
题目描述
We have two types of tiles: a 2x1 domino shape, and an “L” tromino shape. These shapes may be rotated.
——————————————————
XX <- domino
XX <- “L” tromino
X
——————————————————
Given N, how many ways are there to tile a 2 x N board? Return your answer 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.)
Note:
- N will be in range
[1, 1000]
.
Difficulty: medium
790. Domino and Tromino Tiling
中文描述
你拥有两种形状的多米诺骨牌,一种是XX这样 2x1 形状的,还有一种是L型的。问铺满 2xN 的空间,一共能有多少种不同的方法。结果对10^9 + 7取余数。
输入格式
输入一个值N
,表示长度。
Examples:
- Input: 3
Output: 5
解释:
总共有5中方法组成 2X3 的矩形
XYZ XXZ XYY XXY XYY
XYZ YYZ XZZ XYY XXY
解答思路
解法一:dp解法
1.可以从图里看出, n=1和n=2 n = 1 和 n = 2 的时候为基础情况,在 n=3 n = 3 开始就能发现一定规律。 n=3 n = 3 的第一行,是由 n=2 n = 2 的情况加上一个2X1的多米诺牌放在末尾组成的。 n=3 n = 3 的第二行的前半部分是由 n=1 n = 1 的情况加上两个2X1的多米诺牌横放,放在末尾组成的。 n=3 n = 3 的第二行的后半部分则是不由 n=1,2 n = 1 , 2 的情况组成的。同理 n=4 n = 4 的第一行,是由 n=3 n = 3 的情况加上一个2X1的多米诺牌放在末尾组成的。 n=4 n = 4 的第二行是由 n=2 n = 2 的情况加上两个2X1的多米诺牌横放,放在末尾组成的。 n=4 n = 4 的第三行的前半部分则是由 n=1 n = 1 加上两个L型的多米诺牌放在末尾组成的。 n=4 n = 4 的第三行的后半部分则是不由 n=1,2,3 n = 1 , 2 , 3 的情况组成的。
2.从上我们的大概可以总结规律,每个 n n 都是由之前的情况配合组成的。 dpn d p n 表示第 2×n 2 × n 个的组合个数。
其公式为 dpn=dpn−1+dpn−2+2×∑i=0n−3dpi,其中dp0=1 d p n = d p n − 1 + d p n − 2 + 2 × ∑ i = 0 n − 3 d p i , 其 中 d p 0 = 1 。 dpn−1 d p n − 1 表示以一个2X1的多米诺牌放在末尾组成的, dpn−2 d p n − 2 表示以两个2X1的多米诺牌横放,放在末尾组成的,剩下的则是由带有L型为末尾的。结尾形状具体可以看下图:
在 n−3 n − 3 以后都是有2种,所以需要乘2.3.上述式子可以简化,用 dpn−dpn−1=dpn−1+dpn−2+2×∑i=0n−3dpi−(dpn−2+dpn−3+2×∑i=0n−4dpi) d p n − d p n − 1 = d p n − 1 + d p n − 2 + 2 × ∑ i = 0 n − 3 d p i − ( d p n − 2 + d p n − 3 + 2 × ∑ i = 0 n − 4 d p i ) ,化简得到 dpn=2dpn−1−dpn−3 d p n = 2 d p n − 1 − d p n − 3 。这就是递推方程。
4.复杂度估计 O(n) O ( n )
解法二:矩阵乘法
1. dpn=dpn−1+dpn−2+2×∑i=0n−3dpi,其中dp0=1 d p n = d p n − 1 + d p n − 2 + 2 × ∑ i = 0 n − 3 d p i , 其 中 d p 0 = 1 ,写成矩阵形式为
[a1b1c1]×⎡⎣⎢⎢⎢⎢dpn−1dpn−2∑i=0n−3dpi⎤⎦⎥⎥⎥⎥=[a2b2c2]×⎡⎣⎢⎢⎢⎢dpn−2dpn−3∑i=0n−4dpi⎤⎦⎥⎥⎥⎥=...=[an−2bn−2cn−2]×⎡⎣⎢dp2dp1dp0⎤⎦⎥=2an−2+bn−2+cn−2 [ a 1 b 1 c 1 ] × [ d p n − 1 d p n − 2 ∑ i = 0 n − 3 d p i ] = [ a 2 b 2 c 2 ] × [ d p n − 2 d p n − 3 ∑ i = 0 n − 4 d p i ] = . . . = [ a n − 2 b n − 2 c n − 2 ] × [ d p 2 d p 1 d p 0 ] = 2 a n − 2 + b n − 2 + c n − 2
其中 a1=1,b1=1,c1=2 a 1 = 1 , b 1 = 1 , c 1 = 2 。
我们取 [ak−1bk−1ck−1]×⎡⎣⎢⎢⎢⎢dpn−k+1dpn−k∑i=0n−k−1dpi⎤⎦⎥⎥⎥⎥ [ a k − 1 b k − 1 c k − 1 ] × [ d p n − k + 1 d p n − k ∑ i = 0 n − k − 1 d p i ]
由于 ak−1×dpn−k+1=ak−1×(dpn−k+dpn−k−1+2∑i=0n−k−2dpi) a k − 1 × d p n − k + 1 = a k − 1 × ( d p n − k + d p n − k − 1 + 2 ∑ i = 0 n − k − 2 d p i )
所以上面矩阵乘法结果为 ak−1×(dpn−k+dpn−k−1+2∑i=0n−k−2dpi)+bk−1×dpn−k+ck−1×∑i=0n−k−1dpi a k − 1 × ( d p n − k + d p n − k − 1 + 2 ∑ i = 0 n − k − 2 d p i ) + b k − 1 × d p n − k + c k − 1 × ∑ i = 0 n − k − 1 d p i
=(ak−1+bk−1)dpn−k+(ak−1+ck−1)dpn−k−1+(2ak−1+ck−1)∑i=0n−k−2dpi = ( a k − 1 + b k − 1 ) d p n − k + ( a k − 1 + c k − 1 ) d p n − k − 1 + ( 2 a k − 1 + c k − 1 ) ∑ i = 0 n − k − 2 d p i
=[akbkck]×⎡⎣⎢⎢⎢⎢dpn−kdpn−k−1∑i=0n−k−2dpi⎤⎦⎥⎥⎥⎥ = [ a k b k c k ] × [ d p n − k d p n − k − 1 ∑ i = 0 n − k − 2 d p i ] ,
可以得到:
ak=ak−1+bk−1bk=ak−1+ck−1ck=2ak−1+ck−1 a k = a k − 1 + b k − 1 b k = a k − 1 + c k − 1 c k = 2 a k − 1 + c k − 1
进一步:
ak=ak−2+bk−2+ak−2+ck−2=2ak−2+bk−2+ck−2bk=3ak−2+bk−2+ck−2ck=4ak−2+bk−2+ck−2 a k = a k − 2 + b k − 2 + a k − 2 + c k − 2 = 2 a k − 2 + b k − 2 + c k − 2 b k = 3 a k − 2 + b k − 2 + c k − 2 c k = 4 a k − 2 + b k − 2 + c k − 2 。
转化为矩阵形式为:
[an−2bn−2cn−2]×⎡⎣⎢211⎤⎦⎥=[an−4bn−4cn−4]×⎡⎣⎢211311421⎤⎦⎥×⎡⎣⎢211⎤⎦⎥=[an−2kbn−2kcn−2k]×⎡⎣⎢211311421⎤⎦⎥k−1×⎡⎣⎢211⎤⎦⎥ [ a n − 2 b n − 2 c n − 2 ] × [ 2 1 1 ] = [ a n − 4 b n − 4 c n − 4 ] × [ 2 3 4 1 1 2 1 1 1 ] × [ 2 1 1 ] = [ a n − 2 k b n − 2 k c n − 2 k ] × [ 2 3 4 1 1 2 1 1 1 ] k − 1 × [ 2 1 1 ]
如果 n n 为偶数,最小从下标2开始
[a2b2c2]=[a1+b1a1+c12a1+c1]=[234] [ a 2 b 2 c 2 ] = [ a 1 + b 1 a 1 + c 1 2 a 1 + c 1 ] = [ 2 3 4 ]
如果 n n 为奇数最小从下标1开始
[a1b1c1]=[112] [ a 1 b 1 c 1 ] = [ 1 1 2 ]
这样我们可以用大数乘法的方法,只用 O(logn) O ( l o g n ) 的时间完成矩阵乘法3.复杂度估计 O(logn) O ( l o g n ) O(logN) time O(1) space linear algebraic algorithm (in python)
代码
解法一,
class Solution(object):
def numTilings(self, N):
"""
:type N: int
:rtype: int
35MS
"""
if N == 1:
return 1
if N == 2:
return 2
dp = [1 for _ in range(N + 1)]
dp[2] = 2
for i in range(3, N + 1):
dp[i] = (2 * dp[i - 1] + dp[i - 3]) % int(1e9 + 7)
return dp[-1]
解法二,使用numpy库,直接使用矩阵乘法:
class Solution(object):
def numTilings(self, N):
"""
:type N: int
:rtype: int
95MS
"""
import numpy as np
if N == 1:
return 1
if N == 2:
return 2
if N % 2 == 1:
k = (N - 2) // 2
# a1,b1,c1
start = np.array([1, 1, 2], dtype=np.int64)
else:
k = (N - 4) // 2
# a2,b2,c2
start = np.array([2, 3, 4], dtype=np.int64)
A = np.array([[2, 3, 4], [1, 1, 2], [1, 1, 1]], dtype=np.int64)
while k > 0:
# 相当于转化为2进制
if k % 2 == 1:
start = np.dot(start, A) % 1000000007
k //= 2
A = A.dot(A) % 1000000007
return int(np.dot(start, np.array([[2],[1],[1]], dtype=np.int64)) % 1000000007)