动态规划—790. 多米诺和托米诺平铺
题目描述
前言
本文将详细讨论LeetCode上的"多米诺和三米诺平铺"问题。这是一个经典的动态规划问题,要求我们计算用多米诺骨牌和三米诺骨牌填充2x n
网格的方法数。我们将从问题定义开始,逐步深入理解问题本质,提出解决方案,并给出Python和C++的具体实现。
基本思路
1. 定义
给定一个整数n
,我们需要计算用1x 2
的多米诺骨牌和L
形的三米诺骨牌填充2 x n
网格的不同方法数。结果需要对10^9 + 7
取模。
2. 理解问题和递推关系
这个问题的关键在于理解如何使用动态规划来解决。我们需要找出填充方式之间的递推关系,并利用这种关系来构建解决方案。
3. 解决方法
我们可以定义dp[i]
为填充2 x i
网格的方法数。关键是要考虑到所有可能的填充方式:
- 使用一个竖直的多米诺骨牌填充最后一列:
dp[i-1]
- 使用两个水平的多米诺骨牌填充最后两列:
dp[i-2]
- 使用一个三米诺骨牌填充最后两列的一部分,这有两种方式
因此,我们可以得到递推公式:
dp[i] = dp[i-1] + dp[i-2] + 2 * (dp[i-3] + ... + dp[0])
4. 进一步优化
我们可以通过数学推导简化上述公式。令S[i] = dp[i-3] + ... + dp[0]
,那么:
S[i] = S[i-1] + dp[i-3]
dp[i] = dp[i-1] + dp[i-2] + 2 * S[i]
这样,我们可以在O(1)时间内计算每个状态,将时间复杂度优化到O(n)。
5. 小总结
通过分析填充方式并建立递推关系,我们成功地将问题转化为了一个动态规划问题。通过数学推导,我们进一步优化了算法的时间复杂度。
以上就是多米诺和托米诺平铺问题的基本思路。
代码实现
Python代码
class Solution:
def numTilings(self, n: int) -> int:
MOD = 10**9 + 7
if n <= 2:
return n
#初始化dp数组
dp = [0] * (n + 1)
dp[0], dp[1], dp[2] = 1, 1, 2
# 初始化S
S = 1
# 动态规划过程
for i in range(3, n + 1):
dp[i] = (dp[i-1] + dp[i-2] + 2 * S) % MOD
S = (S + dp[i-2]) % MOD
return dp[n]
Python代码解释总结
这段Python代码实现了优化后的动态规划算法。我们使用dp
数组存储每个状态的解,使用S
变量来存储优化后的累加和。通过一次遍历,我们可以在O(n)的时间复杂度内求解问题。代码中的取模操作确保了结果在给定范围内。
C++代码
class Solution {
public:
int numTilings(int n) {
const int MOD = 1e9 + 7;
if (n <= 2) return n;
// 初始化dp数组
vector<long long> dp(n + 1, 0);
dp[0] = 1; dp[1] = 1; dp[2] = 2;
// 初始化S
long long S = 1;
// 动态规划过程
for (int i = 3; i <= n; ++i) {
dp[i] = (dp[i-1] + dp[i-2] + 2 * S) % MOD;
S = (S + dp[i-2]) % MOD;
}
return dp[n];
}
};
C++代码解释总结
C++代码的实现逻辑与Python版本相同,都是基于优化后的动态规划算法。使用vector<long long>
来存储dp
数组,以避免整数溢出。同样通过一次遍历完成所有状态的计算,时间复杂度为O(n)。
总结
通过本文的分析和实现,我们深入理解了"多米诺和三米诺平铺"问题的本质,并学习了如何使用动态规划来解决此类组合问题。我们不仅给出了基本的解决方案,还通过数学推导对算法进行了优化,最终得到了时间复杂度为O(n)的高效解法。Python和C++的实现展示了如何将理论转化为实际代码,这对于理解和掌握动态规划技巧很有帮助。
标签:填充,790,Python,代码,多米诺,int,C++,托米,dp From: https://blog.csdn.net/AlbertDS/article/details/143145244