力扣 552. 学生出勤记录 II dp / 矩阵快速幂

129 篇文章 2 订阅
8 篇文章 0 订阅

https://leetcode-cn.com/problems/student-attendance-record-ii/
在这里插入图片描述

思路一:动态规划, d p i , j , k dp_{i,j,k} dpi,j,k表示前 i i i次出勤缺勤了 j j j次且最后连续 k k k次迟到情况下能获得出勤奖励的次数。因为当且仅当 j < 2 & & k < 3 j<2 \&\&k<3 j<2&&k<3时才有意义,所以总体复杂度依然是 O ( n ) O(n) O(n)的。下面是状态转移方程的推导,我们可以分别考虑 j = 0 j=0 j=0 j = 1 j=1 j=1的情况,首先是 j = 0 & & k = 0 j=0\&\&k=0 j=0&&k=0的情况,即第 n n n次到场,有转移方程:
d p i , 0 , 0 = d p i − 1 , 0 , 0 + d p i − 1 , 0 , 1 + d p i − 1 , 0 , 2 dp_{i,0,0}=dp_{i-1,0,0}+dp_{i-1,0,1}+dp_{i-1,0,2} dpi,0,0=dpi1,0,0+dpi1,0,1+dpi1,0,2
k > 0 k>0 k>0时,显然前 i − 1 i-1 i1次出勤最后必须有连续的 k − 1 k-1 k1次迟到,有转移方程:
d p i , 0 , k = d p i − 1 , 0 , k − 1 dp_{i,0,k}=dp_{i-1,0,k-1} dpi,0,k=dpi1,0,k1
接下来考虑 j = 1 & & k = 0 j=1\&\&k=0 j=1&&k=0的情况,此时我们第 n n n次不仅可以选择到场还可以选择缺勤,有转移方程:
d p i , 1 , 0 = d p i − 1 , 1 , 0 + d p i − 1 , 1 , 1 + d p i − 1 , 1 , 2 + d p i − 1 , 0 , 0 + d p i − 1 , 0 , 1 + d p i − 1 , 0 , 2 dp_{i,1,0}=dp_{i-1,1,0}+dp_{i-1,1,1}+dp_{i-1,1,2}+dp_{i-1,0,0}+dp_{i-1,0,1}+dp_{i-1,0,2} dpi,1,0=dpi1,1,0+dpi1,1,1+dpi1,1,2+dpi1,0,0+dpi1,0,1+dpi1,0,2
k > 0 k>0 k>0时,和 j = 0 j=0 j=0的情况是类似的,略过。显然最后的答案就是 ∑ j , k d p n , j , k \sum_{j,k}dp_{n,j,k} j,kdpn,j,k

class Solution {
public:
    int checkRecord(int n) {
        // 前i天有j个A且最后有连续k个L 可获得出勤奖励的情况数
        const int mod=1e9+7;
        using ll=long long;
        vector<array<array<ll,3>,2>> dp(n+1);
        dp[1][0][0]=dp[1][0][1]=dp[1][1][0]=1;
        for(int i=2;i<=n;i++)
        {
            for(int k=0;k<3;k++)
            {
                dp[i][0][0]=(dp[i][0][0]+dp[i-1][0][k])%mod;
                dp[i][1][0]=(dp[i][1][0]+dp[i-1][1][k]+dp[i-1][0][k])%mod;
            }
            for(int k=1;k<3;k++)
            {
                dp[i][0][k]=dp[i-1][0][k-1];
                dp[i][1][k]=dp[i-1][1][k-1];
            }
        }
        ll ans=0;
        for(int j=0;j<2;j++)
            for(int k=0;k<3;k++)
                ans+=dp[n][j][k];
        return ans%mod;
    }
};

思路二:矩阵快速幂。考虑把思路一中后二维合并起来,即 d p i , j ′ = d p i , j / 3 , j % 3 dp_{i,j}'=dp_{i,j/3,j\%3} dpi,j=dpi,j/3,j%3。那么可以得到以下的关系:
d p n , 0 = d p n − 1 , 0 + d p n − 1 , 1 + d p n − 1 , 2 d p n , 1 = d p n − 1 , 0 d p n , 2 = d p n − 1 , 1 d p n , 3 = d p n − 1 , 0 + d p n − 1 , 1 + d p n − 1 , 2 + d p n − 1 , 3 + d p n − 1 , 4 + d p n − 1 , 5 d p n , 4 = d p n − 1 , 3 d p n , 5 = d p n − 1 , 4 \begin{array}{l} dp_{n,0}=dp_{n-1,0}+dp_{n-1,1}+dp_{n-1,2} \\ dp_{n,1}=dp_{n-1,0} \\ dp_{n,2}=dp_{n-1,1} \\ dp_{n,3}=dp_{n-1,0}+dp_{n-1,1}+dp_{n-1,2} + dp_{n-1,3}+dp_{n-1,4}+dp_{n-1,5} \\ dp_{n,4}=dp_{n-1,3} \\ dp_{n,5}=dp_{n-1,4} \\ \end{array} dpn,0=dpn1,0+dpn1,1+dpn1,2dpn,1=dpn1,0dpn,2=dpn1,1dpn,3=dpn1,0+dpn1,1+dpn1,2+dpn1,3+dpn1,4+dpn1,5dpn,4=dpn1,3dpn,5=dpn1,4
用矩阵乘法表达就是:
d p n = d p n − 1 , 0 d p n − 1 , 1 d p n − 1 , 2 d p n − 1 , 3 d p n − 1 , 4 d p n − 1 , 5 ∗ 1 1 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 0 0 dp_n= \begin{array}{l} dp_{n-1,0} & dp_{n-1,1} & dp_{n-1,2}& dp_{n-1,3}& dp_{n-1,4} & dp_{n-1,5} & \end{array} * \begin{array}{l} 1 & 1 & 0 & 1 & 0 & 0\\ 1 & 0 & 1 & 1 & 0 & 0\\ 1 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 & 1 & 0\\ 0 & 0 & 0 & 1 & 0 & 1\\ 0 & 0 & 0 & 1 & 0 & 0\\ \end{array} dpn=dpn1,0dpn1,1dpn1,2dpn1,3dpn1,4dpn1,5111000100000010000111111000100000010
设该矩阵为 M M M,可得: d p n = d p 1 ∗ M n − 1 dp_n=dp_1*M^{n-1} dpn=dp1Mn1。因此利用矩阵快速幂可以把时间复杂度降低到 O ( l g n ) O(lgn) O(lgn)

class Matrix{
public:
    using ll=long long;

    Matrix(){}
    Matrix(const array<array<ll,6>,6> &mx):m(mx){}

    array<ll,6>& operator[](int i)
    {
        return m[i];
    }

    void init()
    {
        for(int i=0;i<6;i++)
            m[i][i]=1;
    }

    void operator *=(Matrix mb)
    {
        int mod=1e9+7;
        Matrix ma;
        for(int i=0;i<6;i++)
        {
            for(int j=0;j<6;j++)
            {
                for(int k=0;k<6;k++)
                {
                    ma[i][j]+=(m[i][k]*mb[k][j]);
                    ma[i][j]%=mod;
                }
            }
        }
        *this=ma;
    }
private:
    array<array<ll,6>,6> m{};
};

Matrix qpow(int n, Matrix m)
{
    Matrix ans;
    ans.init();
    while(n)
    {
        if(n&1)
            ans*=m;
        m*=m;
        n>>=1;
    }
    return ans;
}

class Solution {
public:
    int checkRecord(int n) {
        const int mod=1e9+7;
        Matrix dp;
        dp[0][0]=dp[0][1]=dp[0][3]=1;
        Matrix m({1,1,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,1,0,0});
        dp*=qpow(n-1,m);
        long long ans=0;
        for(int i=0;i<6;i++)
            ans+=dp[0][i];
        return ans%mod;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值