LeetCode 1411. 给 N x 3 网格图涂色的方案数 (计数DP、矩阵加速递推)

1411. 给 N x 3 网格图涂色的方案数

  • 暴力计数DP
    考虑 n n n行的方案数,向 n + 1 n+1 n+1行递推的时候,只需要考虑上一行的状态即可。
    合法的就转移。
    时间复杂度: O ( 27 ∗ 27 ∗ N ) O(27*27*N) O(2727N)
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=2an1+2bn1
b n = 2 ∗ a n − 1 + 3 ∗ b n − 1 b_n = 2*a_{n-1}+3*b_{n-1} bn=2an1+3bn1

利用矩阵加速递推的思想,可以写成。
[ 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]=[an1,bn1](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;
        }
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值