学生的出勤记录II--动态规划和dfs记忆化解决
Posted C_YCBX Py_YYDS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学生的出勤记录II--动态规划和dfs记忆化解决相关的知识,希望对你有一定的参考价值。
题目
题目解析(其实重在连续 L 的处理)
dfs记忆化的思路
这道题浏览后,乍一看就觉得是关于多个变量的 dfs 记忆化操作,我也是朝着这个方向走的,但是问题却出在考虑的情况太多。。我把不够成答案的组合也算在内了,实际上我们每次 dfs 只需要枚举可形成答案的组合即可。所以一旦 A 和 L 的选择达到最大上限,则不可再选。(L较为特殊需要连续的)
基本的dfs框架有了,如何记忆化呢?
有了基本dfs框架,那么记忆化就很简单了,我们用 memo[n][2][3]=>memo[i][j][k]
来记录状态,因为这三个状态量固定后,后面的东西均可形成记忆化,表示到了第 i 天我们已经选择了 j 次 A
和 k 次 L
,由于 L
的次数计数是要连续的,所以只要当前选择的不是 L
则 L 计数永远都是0。
dp数组思路
通过三维dp数组进行状态转移:dp[n][2][3]
。
dp[i][j][k]
:
- 第一维 i 表示当前一共到了第几天。
- 第二维 j 表示这些天里选择 A 的次数。
- 第三维 k 表示这些天里连续选择 L 的次数。
状态转移方程:
对于每一天,我们选择 P 、A 、 L 会有不同的状态转移。
- 当天选择 P 的状态转移方程:
dp[i][j][0] = (dp[i][j][0] + dp[i-1][j][k]) % mod
,左边是当天选择 P 后所有可能出现的状态,它能转移到上一天的所有状态。 - 当天选择 A 的状态转移方程:
dp[i][1][0] = (dp[i][1][0] + dp[i-1][0][k]) % mod
,同样左边也是选择 A 后可能出现的状态,对于 A 由于只能最多选择一次,所以该天状态只能有这一种,而且由于该天状态 A 选择了一次,则上一天只能选择0次了。 - 当天选择L的状态转移方程:
dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k-1]) % mod
,选择 L 后终于可以出现连续的 L 了,所以当天状态为所有可能的状态,而依赖的上一天的状态只能是 k-1 个 L。
注意到这个状态转移的过程是:当天的可能状态推导出可以依赖的上一天的状态。
基本情况:对于
dp[0][][]
对于第0天(也就是第一天)的选择,我们每一种选择只可能有一种组合:
dp[0][0][0] = 1
dp[0][1][0] = 1
dp[0][0][1] = 1
解题代码
dfs记忆化
class Solution {
public:
int checkRecord(int n) {
const int MOD = 1e9+7;
int memo[n+1][2][3];memset(memo,0,sizeof(memo));
function<int(int,int,int)> dfs = [&](int cntA,int cntL,int k){
if(k==n){
return 1;
}
if(memo[k][cntA][cntL])return memo[k][cntA][cntL];
int res = 0;
for(int i=0;i<3;i++){
//不能再选A了或L的情况进行跳过
if(cntA>=1&&i==0)
continue;
if(cntL>=2&&i==1)
continue;
int A_t = cntA,L_t;
if(i==0) {A_t ++;L_t=0;}
else if(i==1) L_t = cntL+1;
else L_t = 0;
res = (res+dfs(A_t,L_t,k+1))%MOD;
}
return memo[k][cntA][cntL] = res;
};
return dfs(0,0,0);
}
};
dp数组状态转移
func checkRecord(n int) (ans int) {
const mod = 1e9 + 7
dp := make([][2][3]int, n) // 三个维度分别表示:长度,A 的数量,结尾连续 L 的数量
dp[0][0][0] = 1
dp[0][1][0] = 1
dp[0][0][1] = 1
for i := 1; i < n; i++ {
// 选择P,只不是选择L的话,L的位置都设为0,因为既然选择了其他的就不可能存在连续的数据
for j := 0; j <= 1; j++ {
for k := 0; k <= 2; k++ {
dp[i][j][0] = (dp[i][j][0] + dp[i-1][j][k]) % mod
}
}
// 选择A,第二维度为0是同理的
for k := 0; k <= 2; k++ {
dp[i][1][0] = (dp[i][1][0] + dp[i-1][0][k]) % mod
}
// 选择L,这样就可以连续了
for j := 0; j <= 1; j++ {
for k := 1; k <= 2; k++ {
dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k-1]) % mod
}
}
}
//加上所有第n-1天的产物
for j := 0; j <= 1; j++ {
for k := 0; k <= 2; k++ {
ans = (ans + dp[n-1][j][k]) % mod
}
}
return ans
}
以上是关于学生的出勤记录II--动态规划和dfs记忆化解决的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 551. 学生出勤记录 I /552. 学生出勤记录 II(动态规划)/345. 反转字符串中的元音字母(set加入元素的方法)
LeetCode 552 学生出勤记录II[动态规划] HERODING的LeetCode之路
leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结