Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 790. Domino and Tromino Tiling #790

Open
grandyang opened this issue May 30, 2019 · 2 comments
Open

[LeetCode] 790. Domino and Tromino Tiling #790

grandyang opened this issue May 30, 2019 · 2 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

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 109 + 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.

Example 1:

**Input:** n = 3
**Output:** 5
**Explanation:** The five different ways are show above.

Example 2:

**Input:** n = 1
**Output:** 1

Constraints:

  • 1 <= n <= 1000

这道题是关于多米诺骨牌和三格骨牌的,其中由两个方形格子组成的是多米诺骨牌(音译,即为双格骨牌),而由三个方形格子组成的‘L’型的是三格骨牌,但其实本质还是个拼格子的问题,并没有利用到骨牌酷炫的连倒技能,倒反而更像是俄罗斯方块中的形状。说是有一个2xN大小的棋盘,我们需要用这些多米诺和三格骨牌来将棋盘填满,问有多少种不同的填充方法,结果需要对一个超大数取余。那么根据博主多年的经验,对于这种求极值,并且超大的情况下,只能使用动态规划 Dynamic Programming 来做,什么暴力递归神马的,等着爆栈吧。

既然决定了要用 DP 来做,那么首先就来设计 dp 数组吧,这里就用一个一维的 dp 数组就行了,其中 dp[i] 表示填满前i列的不同填法总数对超大数 10e^9+7 取余后的结果。DP 解法的难点就是求状态转移方程了,没什么太好的思路的时候,就从最简单的情况开始罗列吧。题目中给了N的范围是 [1, 1000],那么来看:

当 N=1 时,那么就是一个 2x1 大小的棋盘,只能放一个多米诺骨牌,只有一种情况。

当 N=2 时,那么就是一个 2x2 大小的棋盘,如下图所示,有两种放置方法,可以将两个多米诺骨牌竖着并排放,或者是将其横着并排放。

当 N=3 时,那么就是一个 3x2 大小的棋盘,共用五种放置方法,如下图所示。仔细观察这五种情况,可以发现其时时跟上面的情况有联系的。前两种情况其实是 N=2 的两种情况后面加上了一个竖着的多米诺骨牌,第三种情况其实是 N=1 的那种情况后面加上了两个平行的横向的多米诺骨牌,后两种情况是 N=0(空集)再加上两种三格骨牌对角摆开的情况。

当 N=4 时,那么就是一个 4x2 大小的棋盘,共用十一种放置方法,太多了就不一一画出来了,但是其也是由之前的情况组合而成的。首先是 N=3 的所有情况后面加上一个竖着多米诺骨牌,然后是 N=2 的所有情况加上两个平行的横向的多米诺骨牌,然后 N=1 再加上两种三格骨牌对角摆开的情况,然后 N=0(空集)再加上两种三格骨牌和一个横向多米诺骨牌组成的情况。

N=5 的情况博主没有再画了,可以参见ZhengKaiWei大神的帖子中的手稿图,很萌~

根据目前的状况,我们可以总结一个很重要的规律,就是 dp[n] 是由之前的 dp 值组成的,其中 dp[n-1] 和 dp[n-2] 各自能贡献一种组成方式,而 dp[n-3],一直到 dp[0],都能各自贡献两种组成方式,所以状态转移方程呼之欲出:

dp[n] = dp[n-1] + dp[n-2] + 2 * (dp[n-3] + ... + dp[0])

= dp[n-1] + dp[n-3] + dp[n-2] + dp[n-3] + 2 * (dp[n-4] + ... dp[0])

= dp[n-1] + dp[n-3] + dp[n-1]

= 2 * dp[n-1] + dp[n-3]

最后化简后的形式就是最终的状态转移方程了,是不是叼的飞起~

class Solution {
public:
    int numTilings(int N) {
        if (N < 2) return 1;
        int M = 1e9 + 7;
        vector<long> dp(N + 1);
        dp[0] = 1; dp[1] = 1; dp[2] = 2;
        for (int i = 3; i <= N; ++i) {
            dp[i] = (dp[i - 1] * 2 + dp[i - 3]) % M;
        }
        return dp[N];
    }
};

Github 同步地址:

#790

参考资料:

https://leetcode.com/problems/domino-and-tromino-tiling/

https://leetcode.com/problems/domino-and-tromino-tiling/discuss/116513/Java-solution-DP

https://leetcode.com/problems/domino-and-tromino-tiling/discuss/116581/Detail-and-explanation-of-O(n)-solution-why-dpn2*dn-1+dpn-3

https://leetcode.com/problems/domino-and-tromino-tiling/discuss/116664/Schematic-explanation-of-two-equivalent-DP-recurrence-formula

LeetCode All in One 题目讲解汇总(持续更新中...)

(欢迎加入博主的知识星球,博主将及时答疑解惑,并分享刷题经验与总结,快快加入吧~)

知识星球 喜欢请点赞,疼爱请打赏❤️~.~

微信打赏

|

Venmo 打赏


---|---

@Nannew
Copy link

Nannew commented Mar 14, 2023

函数第一行得检查
if (n < 2) return 1;

@grandyang
Copy link
Owner Author

函数第一行得检查 if (n < 2) return 1;

已添加,多谢指出~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants