开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
遇到一个非常有趣的动态规划题目,秋招的时候也见到过这个题,但没有做出来,现在学会也不晚^^
多米诺和托米诺平铺
有两种形状的瓷砖:一种是 2 x 1 的多米诺形,另一种是形如 "L" 的托米诺形。两种形状都可以旋转。
给定整数 n ,返回可以平铺 2 x n 的面板的方法的数量。返回对 109 + 7 取模 的值。
平铺指的是每个正方形都必须有瓷砖覆盖。两个平铺不同,当且仅当面板上有四个方向上的相邻单元中的两个,使得恰好有一个平铺有一个瓷砖占据两个正方形。
示例 1:
输入: n = 3
输出: 5
解释: 五种不同的方法如上所示。
示例 2:
输入: n = 1
输出: 1
思路
首先这道题找规律的话是可以解出来的,但是感觉这种找规律的方法不够常规,也不容易找到其中的规律。我脑海中冒出的想法是动态规划,因为这本身就是状态的叠加过程,但我能力有限,确实是没有做出来,看了题解才明白,故记录下来,说一下自己对这道题的理解。
我们只关注每一列的状态,对于某一列来说,有四种状态:0-两块未得到平铺;1-上方瓷砖得到平铺;2-下方瓷砖得到平铺;3-两块均得到平铺。
对于状态转移,有:
-
dp[i][0] = dp[i - 1][3]第 i 列两块瓷砖未平铺的种类等于第 i - 1 列的两块瓷砖得到平铺的种类,没有其他情况。
-
dp[i][1] = dp[i - 1][0] + dp[i - 1][2]对于第 i 列上方瓷砖平铺,可由第 i - 1 列均未平铺的状态加上一个L型瓷砖得到,也可由第 i - 1 列下方瓷砖平铺的状态加上一个多米诺型瓷砖得到。
-
dp[i][2] = dp[i - 1][0] + dp[i - 1][1]类比dp[i][1]状态
-
dp[i][3] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][3]对于第 i 列铺满的状态,无论第 i - 1 列为何状态,均可通过加瓷砖的方式满足第 i 列铺满,故需要将四种情况累加。
题解
class Solution {
public int numTilings(int n) {
if(n == 1) {
return 1;
}
final int MOD = (int) 1e9 + 7;
long[][] dp = new long[n + 1][4];
dp[0][0] = 0;
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 1;
for(int i = 1; i <= n; i++) {
dp[i][0] = dp[i - 1][3];
dp[i][1] = (dp[i - 1][0] + dp[i - 1][2]) % MOD;
dp[i][2] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
dp[i][3] = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][3]) % MOD;
}
return (int) dp[n][3];
}
}