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。