Dynamic planning of student attendance records

Problem description

Give you an integer \ (n \) to represent the number of days students attend. The possible attendance of students on each day is: 'A' (absence), 'L' (late), 'P' (normal attendance).

If students want to obtain attendance reward, they need to meet the following conditions at the same time:

  • The number of days absent from work in \ (n \) days cannot exceed two
  • In \ (n \) days, you cannot be late for three consecutive days

It is now required to know how many possible attendance situations can be rewarded within \ (n \) days. Since the number of possible cases will be large, it is necessary to find the result \ (1e9 + 7 \)

\Value range of (n \): \ (1 < = n < = 10 ^ 5 \)

Solution ideas

  • DFS

    First, try to use the general DFS method to solve this problem. The attendance of each day can only be one of 'A', 'L' and 'P'. Therefore, you only need to continuously traverse the possible conditions of each position from the starting position, find the qualified attendance and accumulate them

    In particular, if the award-winning conditions are not met, the variable \ (aCnt \) can represent the number of 'A' in the current attendance record, and the variable \ (lCnt \) represents the number of continuous' L 'in the current position. For attendance records that do not meet the conditions, you can exit the search earlier

  • DFS with memory

    During the search process, the previously calculated data will be repeatedly calculated, so it will waste a lot of time. In order to solve this problem, the previously calculated results can be stored, so as to reduce the time complexity of the algorithm.

    How to cache the previously calculated results is A challenging problem, because there are three states, and there are several states in each state, so using three-dimensional array here will be A better solution. By defining \ (cache[i][j][k] \), it indicates the number of possible awards that exist when the status of 'A' is \ (j \) and the status of 'L' is \ (K \) at the position of \ (I \)

  • dynamic programming

    With the existence of the above \ (cache[i][j][k] \), it can now be realized through dynamic planning (the conversion process is troublesome)

    Definition \ (dp[i][j][k] \) indicates the number of possible winners when the 'A' status is \ (j \) and the 'L' status is \ (K \)

    Specifically, the corresponding transfer function is as follows:

    • When the attendance of position \ (i \) is' P ', the corresponding transfer function is as follows

    \[dp[i][j][0] = dp[i][j][0] + \sum_{k=0}^{2}dp[i - 1][j][k] \]

    • When the attendance of position \ (i \) is' A ', the corresponding transfer function

      \[dp[i][1][0] = dp[i][1][0] + \sum_{k=0}^{2} dp[i - 1][0][k] \]

    • When the attendance of position \ (i \) is' L ', the corresponding transfer function

      \[dp[i][j][k] = dp[i][j][k] + dp[i - 1][j][k - 1]\quad 1\leq k \leq 2 \]

  • Matrix fast power

    From the above dynamic programming, it can be concluded that the possible state is \ (dp[i][j][k] \). Since \ (0\leq j \leq 1 \), \ (0\leq k \leq 2 \), the possible state can be simplified to \ (dp[i][6] \), so the final answer is:

    \[ans = \sum_{idx=0}^{6}dp[n][idx] \]

    Convert it to a column vector:

    \[g[n] = \begin{bmatrix} dp[n][0]\\ dp[n][1]\\ dp[n][2]\\ dp[n][3]\\ dp[n][4]\\ dp[n][5]\\ \end{bmatrix} \]

    By conversion logic:

    \[g[n] = \begin{bmatrix}dp[n][0]\\dp[n][1]\\dp[n][2]\\dp[n][3]\\dp[n][4]\\dp[n][5]\\\end{bmatrix} = \begin{bmatrix} dp[n - 1][0] * 1 + dp[n-1][1]*1+dp[i - 1][2]*1+dp[i - 1][3]*0+dp[i - 1][4]*0+dp[i - 1][5]*0\\ dp[n - 1][0] * 1 + dp[n-1][1]*0+dp[i - 1][2]*0+dp[i - 1][3]*0+dp[i - 1][4]*0+dp[i - 1][5]*0\\ dp[n - 1][0] * 0 + dp[n-1][1]*1+dp[i - 1][2]*0+dp[i - 1][3]*0+dp[i - 1][4]*0+dp[i - 1][5]*0\\ dp[n - 1][0] * 1 + dp[n-1][1]*1+dp[i - 1][2]*1+dp[i - 1][3]*1+dp[i - 1][4]*1+dp[i - 1][5]*1\\ dp[n - 1][0] * 0 + dp[n-1][1]*0+dp[i - 1][2]*0+dp[i - 1][3]*1+dp[i - 1][4]*0+dp[i - 1][5]*0\\ dp[n - 1][0] * 0 + dp[n-1][1]*0+dp[i - 1][2]*0+dp[i - 1][3]*0+dp[i - 1][4]*1+dp[i - 1][5]*0\\ \end{bmatrix} \]

    Define matrix \ (mat \)

    \[mat = \begin{bmatrix} 1&1&1&0&0&0\\ 1&0&0&0&0&0\\ 0&1&0&0&0&0\\ 1&1&1&1&1&1\\ 0&0&0&1&0&0\\ 0&0&0&0&1&0\\ \end{bmatrix} \]

    Therefore:

    \[\begin{equation} \begin{split} g[n]&= mat*g[n-1] \\ &= mat*mat*......g[0] \\ &=mat^n*g[0] \end{split} \end{equation} \]

    Through the fast power of the matrix, it can be completed in the time complex reading of \ (O(log_2n) \)

realization

  • DFS

    class Solution {
        int mod = (int) 1e9 + 7;
        int n;
    
        public int checkRecord(int n) {
            this.n = n;
    
            return dfs(0, 0, 0);
        }
    
        /**
        * @param idx: Current processing location
        * @param aCnt: Quantity of 'A' in attendance record of current period
        * @param lCnt: The number of recent consecutive 'L' in the current attendance record
        */
        private int dfs(int idx, int aCnt, int lCnt) {
            if (aCnt >= 2) return 0;
            if (lCnt >= 3) return 0;
            if (idx == n) return 1;
    
            int cnt = 0;
            cnt += dfs(idx + 1, aCnt + 1, 0); // Current position is' A '
            cnt %= mod;
            cnt += dfs(idx + 1, aCnt, lCnt + 1); // Current position is' L '
            cnt %= mod;
            cnt += dfs(idx + 1, aCnt, 0); // Current position is' P '
            cnt %= mod;
    
            return cnt;
        }
    }
    

    Complexity analysis:

    ​ Time complexity: search is required for each location, so the time complexity is \ (O(3^n) \)

    ​ Space complexity: ignoring the stack overhead caused by recursion, the space complexity is \ (O(1) \)

  • DFS with memory

    class Solution {
        int mod = (int) 1e9 + 7;
        int n;
        int[][][] cache; // Cache for storing intermediate calculation results
    
        public int checkRecord(int n) {
            this.n = n;
            this.cache = new int[n][2][3];
    
            // Initialize each element to - 1, indicating that this element has not been accessed
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < 2; ++j) {
                    for (int k = 0; k < 3; ++k)
                        cache[i][j][k] = -1;
                }
            }
    
            return dfs(0, 0, 0);
        }
    
        private int dfs(int idx, int aCnt, int lCnt) {
            if (aCnt >= 2) return 0;
            if (lCnt >= 3) return 0;
            if (idx == n) return 1;
    
            if (cache[idx][aCnt][lCnt] != -1)
                return cache[idx][aCnt][lCnt];
    
            cache[idx][aCnt][lCnt] = 0; // Once accessed, first set the calculation result of this location to 0, indicating that the result of this location has been calculated
    
            cache[idx][aCnt][lCnt] += dfs(idx + 1, aCnt + 1, 0); // Current position is' A '
            cache[idx][aCnt][lCnt] %= mod;
    
            cache[idx][aCnt][lCnt] += dfs(idx + 1, aCnt, lCnt + 1); // Current position is' L '
            cache[idx][aCnt][lCnt] %= mod;
    
            cache[idx][aCnt][lCnt] += dfs(idx + 1, aCnt, 0); // Current position is' P '
            cache[idx][aCnt][lCnt] %= mod;
    
            return cache[idx][aCnt][lCnt];
        }
    }
    

    Complexity analysis:

    ​ Time complexity: a total of \ (n*2*3 \) states need to be enumerated, so the time complexity is \ (O(n) \)

    ​ Space complexity: additional space of \ (n*2*3 \) is needed to record the intermediate calculation results, so the space complexity is \ (O(n) \)

  • dynamic programming

    class Solution {
        int mod = (int) 1e9 + 7;
    
        public int checkRecord(int n) {
            int[][][] dp = new int[n + 1][2][3];
    
            dp[0][0][0] = 1;
            for (int i = 1; i <= n; ++i) {
                for (int j = 0; j < 2; ++j) {
                    for (int k = 0; k < 3; ++k) {
                        /*
                        	When the current position is' A ', because' A 'is inserted into the current position, the consecutive number of' L 'will be reset to 0, so k is also 0
                        */
                        if (j == 1 && k == 0) {
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 1][0]) % mod;
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 1][1]) % mod;
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 1][2]) % mod;
                        }
                        
                        /*
                        	When the current position is' L ', the continuous' L' number needs to be counted, so it needs to be converted from the 'L' number of k - 1
                        */
                        if (k != 0) {
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1]) % mod;
                        }
                        
                        /*
                        	When the current position is' P ', the continuous number of' L 'will also be reset, so k must also be 0 at this time
                        */
                        if (k == 0) {
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][0]) % mod;
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][1]) % mod;
                            dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][2]) % mod;
                        }
                    }
                }
            }
    
            int ans = 0;
            // Accumulate the possible numbers of the three cases in the last position, that is, the final possible number
            for (int j = 0; j < 2; ++j) {
                for (int k = 0; k < 3; ++k) {
                    ans += dp[n][j][k];
                    ans %= mod;
                }
            }
    
            return ans;
        }
    }
    

    Complexity analysis:

    ​ Time complexity: \ (O(n) \)

    ​ Space complexity: \ (O(n) \)

  • Matrix fast power

    class Solution {
        int N = 6;
        int mod = (int)1e9+7;
        
        // Multiply two matrices
        long[][] mul(long[][] a, long[][] b) {
            int r = a.length, c = b[0].length, z = b.length;
            long[][] ans = new long[r][c];
            for (int i = 0; i < r; i++) {
                for (int j = 0; j < c; j++) {
                    for (int k = 0; k < z; k++) {
                        ans[i][j] += a[i][k] * b[k][j];
                        ans[i][j] %= mod;
                    }
                }
            }
            return ans;
        }
        
        public int checkRecord(int n) {
            long[][] ans = new long[][]{
                {1}, {0}, {0}, {0}, {0}, {0}
            };
            long[][] mat = new long[][]{
                {1, 1, 1, 0, 0, 0},
                {1, 0, 0, 0, 0, 0},
                {0, 1, 0, 0, 0, 0},
                {1, 1, 1, 1, 1, 1},
                {0, 0, 0, 1, 0, 0},
                {0, 0, 0, 0, 1, 0}
            };
            // Matrix fast power calculation part
            while (n != 0) {
                if ((n & 1) != 0) ans = mul(mat, ans);
                mat = mul(mat, mat);
                n >>= 1;
            }
            // End of matrix calculation
            
            int res = 0;
            // Add the column vector to get the final result
            for (int i = 0; i < N; i++) {
                res += ans[i][0];
                res %= mod;
            }
            return res;
        } 
    }
    

    Complexity analysis:

    ​ Time complexity: the time complexity of using the fast power of the matrix is \ ((long_2n) \), and the time complexity of other operations is \ (O(1) \), so the total time complexity is \ (O(long_2n) \)

    ​ Space complexity: \ (O(1) \)

reference resources:

[1] https://leetcode-cn.com/problems/student-attendance-record-ii/solution/gong-shui-san-xie-yi-ti-san-jie-ji-yi-hu-fdfx/

Posted by mumford on Sat, 06 Nov 2021 11:52:22 -0700