2025 Summer Day11
Content:矩阵 DP
Date:2025.7.27
Review
矩阵基本操作:link
Example1 - 洛谷-P1962 斐波那契数列
题目描述
给定 ,求斐波那契数列的第 项 。
数据范围:。
思路
首先显然 的递推是不行的了,我们考虑将递推式转化为矩阵乘法的形式:
这里只需要矩阵快速幂即可。复杂度 。
提交记录:link
Example2 - UVA11270 Tiling Dominoes
题目描述 给定一个 的网格,求用 的方块覆盖网格的方案数。 数据范围:。
思路
我们考虑轮廓线 DP,定义 表示当前修改的点是 ,其状态为 。这里的 和之前题目里的不同,它表示的是第 行的前 列的覆盖情况和第 行的后 列的覆盖情况,其中 表示还未被覆盖, 表示已经被覆盖。
的第一维可以省略,所以转化为 表示考虑到第 列,其状态为 的方案数。
接下来考虑转移,设上一行的状态为 ,转移有三种。
- 当前位置不放,留空 (前提条件:):
- 当前位置横着放 (前提条件: 且 ):
- 当前位置竖着放 (前提条件: 且 ):。
这样就写完了。
Code
#include <bits/stdc++.h>
using std::cin;using std::cout;
constexpr int N = 105;int n, m;long long dp[2][1 << 11];
int main() { std::ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
while (cin >> n >> m) { if (n < m) std::swap(n, m);
int line = 0, max_status = 1 << m; std::memset(dp, 0, sizeof(dp)); dp[line][max_status - 1] = 1;
for (int i = 1; i <= n; i++) { for (int j = 0; j < m; j++) { line = !line; std::memset(dp[line], 0, sizeof(dp[line]));
for (int status = 0; status < max_status; status++) { if (status >> j & 1) { dp[line][status ^ (1 << j)] += dp[!line][status]; } if (j > 0 && (status >> (j - 1) & 1) == 0 && (status >> j & 1)) { dp[line][status | (1 << (j - 1))] += dp[!line][status]; } if (i > 0 && (status >> j & 1) == 0) { dp[line][status | (1 << j)] += dp[!line][status]; } } } }
cout << dp[line][max_status - 1] << '\n'; }
return 0;}Example3 - 洛谷-P5678 河神
思路
我们一样考虑将递推式转化成矩阵乘法的形式。
但是这里我们注意到地推中的操作是 和 ,所以哦我们要做 矩阵乘法 (这个需要证明 操作对 操作具有分配律,这里就不证明了)。
所以就可以使用矩阵快速幂了。
提交记录:link。
Comments
Quiet notes for this article.