题目来源:https://leetcode-cn.com/problems/student-attendance-record-ii/
大致题意:
给出一个n,代表出勤n天。求出有多少种出勤方式(由P、A、L组成)可以让学生拿到出勤奖励。
出现以下两个条件之一取消出勤奖励:
- 有超过一次的A出现
- 连续出现L三次或更多次
思路
动态规划
使用三维数组dp[i][j][k]表示第 i 天时,从开始到当前天,有 j 个 A,当前结尾有 k 个 L 的出勤记录数量。
- 初始化时,dp[0][0][0] = 1,表示第0天,A和L当然也为0,只有一种情况
- 若当前天记录是 P ,那么 A 数量保持不变,结尾 L 数量清零
- 若当前天记录是 A,那么之前的 A 数量只能是0,结尾 L 数量清零
- 若当前天记录是 L,那么 A 数量保持不来,结尾 L 数量加1(之前的L数量只能是 0 或 1 )
- 最终遍历第 n 天的所有 A 和 L 对应的情况,即为所有的出勤方式
代码:
public int checkRecord(int n) {
int ans = 0;
final int MOD = 1000000007;
// 注释掉的方式空间复杂度更高
// int[][][] dp = new int[n+1][2][3]; // 第几个数 当前结尾为A 当前结尾为L
int[][] dp = new int[2][3];
// 初始化,0个数的情况只有一种
// dp[0][0][0] = 1;
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
int[][] dpNew = new int[2][3];
// 以P结尾
// A 不变,L 清零
// 对于每个可行的A,遍历更新所有的L
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
// dp[i][j][0] = (dp[i][j][0] + dp[i-1][j][k]) % MOD;
dpNew[j][0] = (dpNew[j][0] + dp[j][k]) % MOD;
}
}
// 以A结尾
// A 加1,L清零
// 对于上一个A是0的情况,遍历更新所有的L
for (int k = 0; k <= 2; k++) {
// dp[i][1][0] = (dp[i][1][0] + dp[i-1][0][k]) % MOD;
dpNew[1][0] = (dpNew[1][0] + dp[0][k]) % MOD;
}
// 以L结尾
// A 不变,L加1
// 对于每个可行的A,更新所有的可行的L
for (int j = 0; j <= 1; j++) {
for (int k = 1; k <= 2; k++) {
// dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k-1]);
dpNew[j][k] = (dpNew[j][k] + dp[j][k-1]) % MOD;
}
}
dp = dpNew;
}
// 遍历第 n 天的所有 A 和 L 对应的情况
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
// ans = (ans + dp[n][j][k]) % MOD;
ans = (ans + dp[j][k]) % MOD;
}
}
return ans;
}