• 551. Student Attendance Record I + Student Attendance Record II


    ▶ 一个学生的考勤状况是一个字符串,其中各字符的含义是:A 缺勤,L 迟到,P 正常。如果一个学生考勤状况中 A 不超过一个,且没有连续两个 L(L 可以有多个,但是不能连续),则称该学生达标(原文表述:A student could be rewarded if his attendance record doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late). )

    ▶ 551. 给定一个学生的考勤状况,判断他能否达标。

    ● 代码,4 ms,简单的计数,最快的解法算法与之相同

     1 class Solution
     2 {
     3 public:
     4     bool checkRecord(string s)
     5     {
     6         int i, a, l;
     7         for (i = a = l = 0; i < s.size(); i++)
     8         {
     9             if (s[i] == 'A')
    10                 a++;
    11             if (s[i] == 'L')
    12                 l++;
    13             else            // 出现 A 或 P 时均要清空 L 计数
    14                 l = 0;
    15             if (a >= 2 || l > 2)
    16                 return false;
    17         }
    18         return true;
    19     }
    20 };

    ▶ 552. 给定正整数 n,考虑所有长度为 n 的考勤状况(3n 种可能),计算达标的考勤状况数,该数字可能非常大,取关于 109 + 7(竟然是个素数)模作为输出结果

    ● 自己的代码,29 ms,时间复杂度 O(n),考虑递推数列,一个达标序列只能是以下三种情况:

    a[ k ] 表长度为 k,以 P 结尾,不含 A 的达标序列个数

    b[ k ] 表长度为 k,以 PL 结尾,不含 A 的达标序列个数(不能是 LLLP 结尾)

    c[ k ] 表长度为 k,以 LL 结尾,不含 A 的达标序列个数(不能是 LLL 结尾)

    注意到 a[ 1 ] = b[ 1 ] = 1,c[ 1 ] = 0,a[ k ] = a[ k - 1 ] + b[ k - 1 ] + c[ k - 1](任意一种达标序列添个 P),b[ k ] = a[ k - 1 ](P 结尾的达标序列添个 L),c[ k ] = b[ k - 1 ](PL 结尾的达标序列添个 L)

    约化后就变成了 a[ 1 ] = 1,a[ 2 ] = 2,a[ 3 ] = 4,a[ n ] = a[ n - 1 ] + a[ n - 2 ] + a[ n - 3 ]  ( n ≥ 4 )

    对上述数列打表,使用长度为 n+1 的表 f,其中 a[ k ] = f[ k - 1 ],即所有下标有一单位的平移,在代码中注意控制

    因为打表序列中 A 至多一个,所以按照 A 的位置分类讨论:

    ① 不含A,一共有 a[ n ] + b[ n ] + c[ n ] = a[ n + 1 ] 种情况,等于 f[ n ](打表要长一格的原因)

    ② A 在开头,一共有 a[ n - 1 ] + b[ n - 1 ] + c[ n - 1 ] = a[ n ] 种情况,等于 f[ n - 1 ]

    ③ A 在结尾,同上,等于 f[ n - 1 ]

    ④ A 在中间,考虑 A 的左边有长度为 i 的不含 A 的序列,且 A 的右边有长度为 n - i - 1 的不含A的序列,一共是 ( a[ i ] + b[ i ] + c[ i ] ) * ( a[ n - i - 1 ] + b[ n - i - 1 ] + c[ n - i - 1 ] ),即 f[ i ] * f[ n - i - 1 ],
    i 在 i = 1 到 i = n - 2 之间求和。

    注意每一步取模运算,因为 f 本身是指数级增长的。

     1 class Solution
     2 {
     3 public:
     4     int checkRecord(int n)
     5     {
     6         if (n == 1)
     7             return 3;
     8         const int m = 1000000007;
     9         vector<long long> f(n + 1, 0);
    10         int i,sum;
    11         for (f[0] = 1, f[1] = 2, f[2] = 4, i = 3; i <= n; f[i] = (f[i - 1] + f[i - 2] + f[i - 3]) % m, i++);
    12         for (sum = (f[n] + f[n - 1] * 2) % m, i = 1; i < n - 1; i++)
    13             sum = (sum + f[i] * f[n - i - 1]) % m;
    14         return sum;
    15     }
    16 };

    ● 代码,4 ms,使用一个矩阵的幂来计算递推

      1 #define ENTRY(A, i, j)  ((A)[(i) * n + (j)])
      2 
      3 int64_t* NewMatrix(size_t n)
      4 {
      5     assert(n >= 1);
      6     int64_t* temp = (int64_t*)calloc(n * n, sizeof(int64_t));
      7     assert(temp != NULL);
      8     return temp;
      9 }
     10 
     11 void FreeMatrix(int64_t* A)
     12 {
     13     free(A);
     14 }
     15 
     16 void SetMatrix(int64_t* A, int64_t* B, size_t n)
     17 {
     18     memcpy(A, B, n * n * sizeof(int64_t));
     19 }
     20 
     21 void IdentityMatrix(int64_t* A, size_t n)// 将方阵 A 赋值为单位方阵
     22 {
     23     int i, j;
     24     for (i = 0; i < n; i++)
     25     {
     26         for (j = 0; j < n; j++)
     27             ENTRY(A, i, j) = (i == j);
     28     }
     29 }
     30 
     31 void MatrixMultiply(int64_t* A, int64_t* B, size_t n)// 方阵乘法 A = A * B
     32 {
     33     assert(n >= 1);
     34     int64_t* C = NewMatrix(n);
     35     int i, j, k;
     36     for (i = 0; i < n; ++i)
     37     {
     38         for (j = 0; j < n; ++j)
     39         {
     40             ENTRY(C, i, j) = 0;
     41             for (k = 0; k < n; ++k)
     42                 ENTRY(C, i, j) += ENTRY(A, i, k) * ENTRY(B, k, j);            
     43         }
     44     }
     45     memcpy(A, C, n * n * sizeof(int64_t));
     46     FreeMatrix(C);
     47 }
     48 
     49 void MatrixPower(int64_t* C, int64_t* A, size_t n, int m)// 方阵幂 C = A ^ m 
     50 {
     51     assert(n >= 1);
     52     assert(m >= 0);
     53     int64_t* B = NewMatrix(n);
     54     SetMatrix(B, A, n);
     55     IdentityMatrix(C, n);
     56     for (; m > 0; m /= 2)// 二进制方法
     57     {
     58         if (m % 2 == 1)
     59             MatrixMultiply(C, B, n);
     60         MatrixMultiply(B, B, n);        
     61     }
     62     FreeMatrix(B);
     63 }
     64 
     65 void MatrixModulus(int64_t* A, size_t n, int64_t modulus)// 方阵逐格求模 A %= m
     66 {
     67     int i, j;
     68     for (i = 0; i < n; ++i)
     69     {
     70         for (j = 0; j < n; ++j)
     71             ENTRY(A, i, j) = ENTRY(A, i, j) % modulus;
     72     }
     73 }
     74 
     75 void MatrixModulusPower(int64_t* C, int64_t* A, size_t n, int m, int64_t modulus)// C = A ^ m % modulus
     76 {
     77     assert(n >= 1);
     78     assert(m >= 0);
     79     int64_t* B = NewMatrix(n);
     80     SetMatrix(B, A, n);
     81     IdentityMatrix(C, n);
     82     for (; m > 0;m/=2)
     83     {
     84         if (m % 2 == 1)
     85         {
     86             MatrixMultiply(C, B, n);
     87             MatrixModulus(C, n, modulus);
     88         }
     89         MatrixMultiply(B, B, n);
     90         MatrixModulus(B, n, modulus);        
     91     }
     92     FreeMatrix(B);
     93 }
     94 
     95 int AttendanceNumber(int m)
     96 {
     97     assert(m >= 0);
     98     size_t n = 6;
     99     int64_t initial_vector[6] = { 1, 3, 8, 19, 43, 94 };
    100     int64_t modulus = 1000000007;
    101     int64_t* A = NewMatrix(n);
    102     ENTRY(A, 0, 1) = 1;
    103     ENTRY(A, 1, 2) = 1;
    104     ENTRY(A, 2, 3) = 1;
    105     ENTRY(A, 3, 4) = 1;
    106     ENTRY(A, 4, 5) = 1;
    107     ENTRY(A, 5, 0) = -1;
    108     ENTRY(A, 5, 1) = -2;
    109     ENTRY(A, 5, 2) = -3;
    110     ENTRY(A, 5, 3) = 0;
    111     ENTRY(A, 5, 4) = 1;
    112     ENTRY(A, 5, 5) = 2;
    113     int64_t answer = 0;
    114     if (m <= 5)
    115         answer = initial_vector[m];
    116     else
    117     {
    118         int64_t* C = NewMatrix(n);
    119         MatrixModulusPower(C, A, n, m - n + 1, modulus);
    120         for (size_t i = 0; i < n; ++i)
    121             answer += ENTRY(C, n - 1, i) * initial_vector[i];
    122         answer = (answer % modulus + modulus) % modulus;
    123         FreeMatrix(C);
    124     }
    125     FreeMatrix(A);
    126     return answer;
    127 }
    128 
    129 #undef ENTRY
    130 
    131 class Solution
    132 {
    133 public:
    134     int checkRecord(int n)
    135     {
    136         return AttendanceNumber(n);
    137     }
    138 };

    ● 初版动态规划(MLE),f[ i ][ j ][ k ] 表示长度为 i,至多 j 个 A,至多 k 个连续 L 的序列。所求目标为 f[ n ][ 1 ][ 2 ],转移方程如下,发现可以用矩阵幂来一次性计算 f[ n ]

      

     1 class Solution
     2 {
     3 public:
     4     int checkRecord(int n)
     5     {
     6         const int MOD = 1000000007;
     7         vector<vector<vector<int>>> f(n + 1, vector<vector<int>>(2, vector<int>(3, 0)));
     8         int i, j, k, val;
     9         for (i = 0; i < 2; i++)
    10             for (j = 0; j < 3; f[0][i][j++] = 1);        
    11         for (i = 1; i <= n; i++)
    12         {
    13             for (j = 0; j < 2; j++)
    14             {
    15                 for (k = 0; k < 3; k++)
    16                 {
    17                     val = f[i - 1][j][2];                       // P
    18                     if (j > 0)                                  // A
    19                         val = (val + f[i - 1][j - 1][2]) % MOD;  
    20                     if (k > 0)                                  // L
    21                         val = (val + f[i - 1][j][k - 1]) % MOD;  
    22                     f[i][j][k] = val;
    23                 }
    24             }
    25         }
    26         return f[n][1][2];
    27     }
    28 };

    ● 改进动态规划,8 ms,直接用矩阵幂来计算

     1 class Solution
     2 {
     3 public:
     4     const int MOD = 1000000007, M = 6;
     5     void mul(vector<vector<int>>& A, vector<vector<int>>& B)
     6     {
     7         vector<vector<int>> temp(M, vector<int>(M, 0));
     8         int i, j, k;
     9         for (i = 0; i < M; i++)
    10         {
    11             for (j = 0; j < M; j++)
    12             {
    13                 for (k = 0; k < M; k++)
    14                     temp[i][j] = (int)((temp[i][j] + (long)A[i][k] * B[k][j]) % MOD);
    15             }
    16         }
    17         A = temp;
    18         return;
    19     }
    20     void pow(vector<vector<int>>& A, int n)
    21     {
    22         vector<vector<int>> temp(M, vector<int>(M, 0));
    23         for (int i = 0; i < M; temp[i][i] = 1, i++);
    24         for (; n > 0; n /= 2)
    25         {
    26             if (n % 2)
    27                 mul(temp, A);
    28             mul(A, A);
    29         }
    30         A = temp;
    31         return;
    32     }
    33     int checkRecord(int n)
    34     {
    35         vector<vector<int>> A = 
    36         {
    37             { 0, 0, 1, 0, 0, 0 },
    38             { 1, 0, 1, 0, 0, 0 },
    39             { 0, 1, 1, 0, 0, 0 },
    40             { 0, 0, 1, 0, 0, 1 },
    41             { 0, 0, 1, 1, 0, 1 },
    42             { 0, 0, 1, 0, 1, 1 },
    43         };
    44         pow(A, n + 1);
    45         return A[5][2];
    46     }
    47 };

    ● 代码,深度优先遍历,TLE,说明穷举肯定不行

     1 class Solution
     2 {
     3 public:
     4     int checkRecord(int n)
     5     {
     6         long long res = 0;
     7         dfs(n, 0, 0, 0, res);
     8         return res % 1000000007;
     9     }
    10     void dfs(int n, int s, int A, int L, long long& res)// 当前共 s 位,有 A 个 A 和 L 个 L
    11     {
    12         if (s == n)
    13         {
    14             res++;
    15             return;
    16         }
    17         dfs(n, s + 1, A, 0, res);   //add "P"
    18         if (A < 1)
    19             dfs(n, s + 1, A + 1, 0, res);
    20         if (L <= 1)
    21             dfs(n, s + 1, A, L + 1, res);
    22     }
    23 };
  • 相关阅读:
    h5红包雨
    Reflect
    el-dialog对话弹框中根据后台数据无限制添加el-select标签,并进行展示,搜索,删除
    jQuery伪分页效果
    canvas实现验证码
    jQuery四叶草菜单效果,跟360杀毒软件差不多
    事件
    传参
    在shell script中进行数值运算的两种方法
    为maven插件设置参数的三种方法
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8387655.html
Copyright © 2020-2023  润新知