矩阵幂求解骨牌覆盖数(SOJ 3021)
题意:给出$4\times N$的矩形以及尺寸为$2\times 1$的骨牌,求解该矩形能被骨牌覆盖的种数。
分析:起初我自己一直尝试推导出一个递推式,但是一直没有成功。后来看了网上别人给的递推式:$f(n)=f(n-1)+5*f(n-2)+f(n-3)-f(n-4)$,有了这个式子,这个题很好求解了。下面我介绍一种其他的方法。将矩形看作$N\times 4$, 铺骨牌到第$i$行有$6$种情况:$XXXX$, $XOOX$, $OXXO$, $XXOO$, $OOXX$, $OOOO$, 其中$X$表示有骨牌,$O$表示没有骨牌。定义$f(i,1)$, $f(i,2)$, $f(i,3)$, $f(i,4)$, $f(i,5)$, $f(i,6)$分别为上述6种情形的种类数,则我们可以得到以下递推关系:
$f(i+1,1)=f(i,1)+f(i,3)+f(i,4)+f(i,5)+f(i,6)$
$f(i+1,2)=f(i,3)$
$f(i+1,3)=f(i,1)+f(i,2)$
$f(i+1,4)=f(i,1)+f(i,5)$
$f(i+1,5)=f(i,1)+f(i,4)$
$f(i+1,6)=f(i,1)$
矩阵形式为:
$\left[\begin{array}{c}f(i+1, 1)\\ f(i+1, 2)\\ f(i+1, 3)\\ f(i+1, 4)\\ f(i+1, 5)\\ f(i+1, 6)\end{array}\right]=\left[\begin{array}{cccccc}1 &0 &1 &1 &1 &1\\ 0 &0 &1 &0 &0 &0\\ 1 &1 &0 &0 &0 &0\\ 1 &0 &0 &0 &1 &0\\ 1 &0 &0 &1 &0 &0\\ 1 &0 &0 &0 &0 &0 \end{array}\right]\left[\begin{array}{c}f(i, 1)\\ f(i, 2)\\ f(i, 3)\\ f(i, 4)\\ f(i, 5)\\ f(i, 6)\end{array}\right]$. 下面的问题即转化成了矩阵的快速幂算法。
代码:
1 #include<iostream> 2 #include<string.h> 3 using namespace std; 4 int N, M; 5 struct matrix 6 { 7 int P[7][7]; 8 matrix() 9 { 10 memset(P, 0, sizeof(P)); 11 } 12 }; 13 matrix matMul(matrix A, matrix B) 14 { 15 matrix C; 16 int i, j, k; 17 for (i = 1; i <= 6; i++) 18 for (j = 1; j <= 6; j++) 19 for (k = 1; k <= 6; k++) 20 C.P[i][j] = (C.P[i][j] + A.P[i][k] * B.P[k][j]) % M; 21 return C; 22 } 23 matrix matPow(matrix M) 24 { 25 matrix Res; 26 int i; 27 for (i = 1; i <= 6; i++) 28 Res.P[i][i] = 1; 29 int k = N - 1; 30 while (k) 31 { 32 if (k & 1) 33 Res = matMul(Res, M); 34 k >>= 1; 35 M = matMul(M, M); 36 } 37 return Res; 38 } 39 int main() 40 { 41 matrix Con,Ans; 42 Con.P[1][1] = Con.P[1][3] = Con.P[1][4] = Con.P[1][5] = Con.P[1][6] = 1; 43 Con.P[2][3] = 1; 44 Con.P[3][1] = Con.P[3][2] = 1; 45 Con.P[4][1] = Con.P[4][5] = 1; 46 Con.P[5][1] = Con.P[5][4] = 1; 47 Con.P[6][1] = 1; 48 while (scanf("%d%d", &N, &M) == 2 && N > 0) 49 { 50 Ans = matPow(Con); 51 printf("%d\n", (Ans.P[1][1] + Ans.P[1][3] + Ans.P[1][4] + Ans.P[1][5] + Ans.P[1][6])%M); 52 } 53 return 0; 54 }