动态规划-矩阵加速

矩阵乘法

{2x+y=33x+2y=9 \left\{ \begin{aligned} 2x + y = 3 \\ 3x + 2y = 9 \end{aligned} \right.

可表示成

[2132]×[xy]=[2x+y3x+2y] \begin{bmatrix} 2 & 1 \\ 3 & 2 \end{bmatrix} \times \begin{bmatrix} x \\ y \end{bmatrix}= \begin{bmatrix} 2x+y \\ 3x+2y \end{bmatrix}

AAm×nm\times n 矩阵,BBn×pn\times p 矩阵。

A×B=CA\times B = C

CCm×pm\times p 矩阵

C=(ci,j)ci,j=k=1nai,k×bk,j C = (c_{i,j})\\ c_{i,j} =\sum_{k=1}^n a_{i,k}\times b_{k,j}

形象理解

C=ci,j=AC =c_{i,j} = A 的第 ii 行逐个乘 BB 的第 jj

非常形象的解释

结合律证明

AAm×nm\times n 矩阵,BBn×pn\times p 矩阵,CCp×qp \times q 的矩阵。

A×B×C=A×(B×C)A×B=DD=(di,j)di,j=k=1nai,kbk,jD×C=Eei,j=l=1p(di,lcl,j)=l=1p((k=1nai,kbk,l)×cl,j)=l=1p(k=1nai,kbk,lcl,j)B×C=Ffi,j=l=1pbi,lcl,jA×F=Ggi,j=k=1n(ai,kfk,j)=k=1n(ai,k×(l=1pbk,lcl,j))=k=1n(l=1pai,kbk,lcl,j) A\times B \times C = A \times (B \times C)\\ A \times B = D \\ D = (d_{i,j}) \\ d_{i,j} = \sum_{k=1}^{n} a_{i,k}\cdot b_{k,j} \\ D \times C = E \\ \begin{aligned} e_{i,j} &= \sum_{l=1}^p (d_{i,l}\cdot c_{l,j}) \\ &= \sum_{l=1}^p ((\sum_{k=1}^{n} a_{i,k}\cdot b_{k,l}) \times c_{l,j}) \\ &= \sum_{l=1}^p (\sum_{k=1}^{n} a_{i,k}\cdot b_{k,l}\cdot c_{l,j}) \\ \end{aligned} \\ B \times C = F \\ f_{i,j} = \sum_{l=1}^p b_{i,l}\cdot c_{l,j} \\ A \times F = G \\ \begin{aligned} g_{i,j} &= \sum_{k=1}^n(a_{i,k}\cdot f_{k,j}) \\ &= \sum_{k=1}^n(a_{i,k}\times (\sum_{l=1}^p b_{k,l}\cdot c_{l,j})) \\ &= \sum_{k=1}^n(\sum_{l=1}^p a_{i,k} \cdot b_{k,l}\cdot c_{l,j}) \end{aligned}

显然

l=1p(k=1nai,kbk,lcl,j)=k=1n(l=1pai,kbk,lcl,j)ei,j=gi,jE=GA×B×C=A×(B×C) \sum_{l=1}^p (\sum_{k=1}^{n} a_{i,k}\cdot b_{k,l}\cdot c_{l,j}) = \sum_{k=1}^n(\sum_{l=1}^p a_{i,k} \cdot b_{k,l}\cdot c_{l,j}) \\ e_{i,j} = g_{i,j} \\ E = G \\ A\times B\times C = A\times(B\times C)

快速幂

2p2|pap=(a2)p/2a^p = (a^2)^{p/2}

2p+12|p+1ap=a(a2)(p1)/2a^p = a \cdot (a^2)^{(p-1)/2}

O(n)O(n) 变成 O(log2n)O(\log_2n)

例题

洛谷 P1939 【模板】矩阵加速(数列)

没什么好说的

由于每项只和前 33 项有关,我们可以开一个 1×31\times 3 的矩阵表示数列。

然后考虑一下转移。

[a1a2a3][a2a3a4]=[a2a3a1+a3] \begin{bmatrix} a_1 & a_2 & a_3 \\ \end{bmatrix} \rightarrow \begin{bmatrix} a_2 & a_3 & a_4 \\ \end{bmatrix}= \begin{bmatrix} a_2 & a_3 & a_1+a_3 \\ \end{bmatrix}

用心感觉一下 转移矩阵就是(也可以解一个九元方程

[001100011] \begin{bmatrix} 0 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 1 \\ \end{bmatrix}

可以有

[a1a2a3]×[001100011]=[a2a3a1+a3]=[a2a3a4] \begin{bmatrix} a_1 & a_2 & a_3 \end{bmatrix} \times \begin{bmatrix} 0 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 1 \\ \end{bmatrix}= \begin{bmatrix} a_2 & a_3 & a_1+a_3 \\ \end{bmatrix}= \begin{bmatrix} a_2 & a_3 & a_4 \\ \end{bmatrix}

踩了洛谷g++的一个坑,sync_with_stdio(false)必须加在所有输入输出之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstring>
typedef long long LL;
using std::cin; using std::cout; using std::cerr; using std::endl; using std::min; using std::max;
const int MOD = 1000000007;
struct matrix{
long long a[5][5], m, n;
void init(const int &nM, const int &nN){
std::memset(this->a, 0, sizeof(this->a));
this->m = nM;
this->n = nN;
}
matrix operator=(const matrix &s){
this->init(s.m, s.n);
for (int i = 0; i <= s.m; ++i)
for (int j = 0; j <= s.n; ++j)
this->a[i][j] = s.a[i][j];
return *this;
}
matrix operator*(const matrix &s){
// if (this->n != s.m)
// cerr << "Matrixs Can't Time";
matrix ans;
ans.init(this->m, s.n);
for (int i = 1; i <= this->m; ++i){
for (int j = 1; j <= s.n; ++j){
for (int k = 1; k <= this->n; ++k){
ans.a[i][j] += this->a[i][k]*s.a[k][j]%MOD;
}
}
}
return ans;
}
}A, B, C;
std::ostream& operator<<(std::ostream &out, const matrix &s){
for (int i = 1; i <= s.m; ++i){
out << '[';
for (int j = 1; j <= s.n-1; ++j){
out << s.a[i][j] << ' ';
}
out << s.a[i][s.n];
out << "]\n";
}
return out;
}
int main(){
int T, n;
std::ios::sync_with_stdio(false);
cin >> T;
while (T--){
cin >> n;
if (n <= 3){
cout << 1 << endl;
continue;
}
A.init(1,3);
A.a[1][1] = A.a[1][2] = A.a[1][3] = 1;
B.init(3,3);
B.a[1][1] = B.a[1][2] = B.a[2][2] = B.a[2][3] = B.a[3][1] = 0;
B.a[1][3] = B.a[2][1] = B.a[3][2] = B.a[3][3] = 1;
n -= 4;
C = B;
while (n){
if (n&1){
C = C * B;
}
B = B * B;
n = n >> 1;
}
A = A * C;
cout << A.a[1][3]%MOD << endl;
}
return 0;
}

摆花

递归很好想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <cstring>
const int MOD = 1000000007;
using std::cin; using std::cout; using std::cerr; using std::endl; using std::min; using std::max;
int dp[2][101], a[101][101], n, m;
int main(){
cin >> n >> m;
dp[1][0] = 1;
// cin.get();
char c;
for (register int i = 1; i <= m; ++i){
dp[1][i] = 1;
for (register int j = 1; j <= m; ++j){
cin >> c;
a[i][j] = c - '0';
}
}
int cur = 0, prev = 1;
for (register int i = 2; i <= n; ++i){
for (register int j = 0; j <= m; ++j){
dp[cur][j] = 0;
for (register int k = 0; k <= m; ++k){
if (a[j][k]) continue;
dp[cur][j] += dp[prev][k];
dp[cur][j] %= MOD;
}
}
std::swap(cur, prev);
}
int ans = 0;
for (int i = 0; i <= m; ++i){
ans += dp[prev][i];
ans %= MOD;
}
cout << ans;
return 0;
}

可以用矩阵加速转移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <cstring>
typedef long long LL;
const int MOD = 1000000007;
using std::cin; using std::cout; using std::cerr; using std::endl; using std::min; using std::max;
int n, m;
struct matrix{
int a[110][110], m, n;
void init(const int &nM, const int &nN){
std::memset(this->a, 0, sizeof(this->a));
this->m = nM;
this->n = nN;
}
matrix operator=(const matrix &s){
this->init(s.m, s.n);
for (int i = 0; i <= s.m; ++i)
for (int j = 0; j <= s.n; ++j)
this->a[i][j] = s.a[i][j];
return *this;
}
matrix operator*(const matrix &s){
matrix ans;
ans.init(this->m, s.n);
for (int i = 1; i <= this->m; ++i){
for (int j = 1; j <= s.n; ++j){
for (int k = 1; k <= this->n; ++k){
ans.a[i][j] += (LL)this->a[i][k]%MOD*(LL)s.a[k][j]%MOD;
ans.a[i][j] %= MOD;
}
}
}
return ans;
}
}A, B;
std::ostream& operator<<(std::ostream &out, const matrix &s){
for (int i = 1; i <= s.m; ++i){
out << '[';
for (int j = 1; j <= s.n-1; ++j){
out << s.a[i][j] << ' ';
}
out << s.a[i][s.n];
out << "]\n";
}
return out;
}
int main(){
cin >> n >> m;
char c;
A.init(m+1,m+1);
for (register int i = 1; i <= m; ++i){
A.a[i][m+1] = 1;
for (register int j = 1; j <= m; ++j){
cin >> c;
A.a[i][j] = c - '0';
A.a[i][j] ^= 1;
}
}
for (register int i = 1; i <= m+1; ++i){
A.a[m+1][i] = 1;
}
B.init(m+1,m+1);
B = A;
int p = n-2;
while (p > 0){
if (p%2!=0) B = B*A;
A = A*A;
p /= 2;
}
matrix ans;
ans.init(1, m+1);
for (int i = 1; i <= ans.n; ++i) ans.a[1][i] = 1;
ans = ans * B;
int sum = 0;
for (int i = 1; i <= ans.n; ++i){
sum += ans.a[1][i];
sum %= MOD;
}
cout << sum;
return 0;
}
作者

Gesrua

发布于

2018-07-07

更新于

2020-11-21

许可协议