- 暴力计数DP
考虑 n n n行的方案数,向 n + 1 n+1 n+1行递推的时候,只需要考虑上一行的状态即可。
合法的就转移。
时间复杂度: O ( 27 ∗ 27 ∗ N ) O(27*27*N) O(27∗27∗N)
class Solution {
public:
typedef long long ll;
int mod = 1e9+7;
int numOfWays(int N) {
ll f[5010][3][3][3] = {0};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
f[1][i][j][k] = check(-1,-1,-1,i,j,k);
for(int n=1;n<N;n++){
for(int i1=0;i1<3;i1++){
for(int j1=0;j1<3;j1++){
for(int k1=0;k1<3;k1++){
for(int i2=0;i2<3;i2++){
for(int j2=0;j2<3;j2++){
for(int k2=0;k2<3;k2++){
if(!check(i1,j1,k1,i2,j2,k2)) continue;
f[n+1][i2][j2][k2] = (f[n+1][i2][j2][k2]+f[n][i1][j1][k1])%mod;
}
}
}
}
}
}
}
ll ans = 0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
ans = (ans+f[N][i][j][k])%mod;
return ans;
}
bool check(int i1,int j1,int k1,int i2,int j2,int k2){
return i1!=i2 && j1!=j2 && k1!=k2 && j2!=i2 && k2!=j2;
}
};
- 递推关系优化、矩阵加速递推
仔细分析一下,上一行的状态,无非两类: A B A ABA ABA型、 A B C ABC ABC型,
于是向下一行转移的时候,只需要考虑下一行的ABA型如何来,ABC型如何来即可。
简单分析一下,
得到如下递推式。
a
n
=
2
∗
a
n
−
1
+
2
∗
b
n
−
1
a_n = 2*a_{n-1}+2*b_{n-1}
an=2∗an−1+2∗bn−1
b
n
=
2
∗
a
n
−
1
+
3
∗
b
n
−
1
b_n = 2*a_{n-1}+3*b_{n-1}
bn=2∗an−1+3∗bn−1
利用矩阵加速递推的思想,可以写成。
[
a
n
,
b
n
]
=
[
a
n
−
1
,
b
n
−
1
]
∗
(
2
2
2
3
)
[a_n,b_n] = [a_{n-1},b_{n-1}]*\begin{pmatrix} 2 & 2 \\ 2 & 3 \end{pmatrix}
[an,bn]=[an−1,bn−1]∗(2223)
于是利用矩阵的快速幂,可以使得时间复杂度降到 l o g ( n ) log(n) log(n)
当然最后的方案数是 a n + b n a_n+b_n an+bn
class Solution {
public:
typedef long long LL;
int mod = 1e9+7;
int numOfWays(int n) {
LL A[2][2] = {2,2,2,3}, B[2][2] = {1,0,0,1};
LL f[2] = {6,6};
quick_mul(A,n-1,B);
LL ans = 0;
for(int i=0;i<2;i++) ans = (ans+f[i]*B[0][i]%mod+f[i]*B[1][i])%mod;
return ans;
}
void matrix_mul(LL A[][2],LL B[][2]){
LL C[2][2] = {0,0,0,0};
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
C[i][j] = (C[i][j]+A[i][k]*B[k][j]%mod)%mod;
}
}
}
memcpy(B,C,sizeof C);
}
void quick_mul(LL A[][2],int n,LL B[][2]){
while(n){
if(n&1) matrix_mul(A,B);
matrix_mul(A,A);
n >>= 1;
}
}
};