题目链接:https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-i/description/
题目大意:给出三个数zero, one, limit
,求满足以下条件的数组的数目:
- 数组中有
zero
个0
和one
个1
- 任何长度大于等于
limit+1
的子数组中都必须包含0
和1
思路:刚开始想的是往zero
个0
里插入one
个1
,找方案数。但不知道怎么满足第二个条件。一看题解是DP,昏了。还是三重DP,这哪想得出来。
不过看了题解后还是比较清晰的,毕竟重点是搞清楚状态转移方程。
设dp0[i][j]
是包含i
个0
和j
个1
且以0
结尾的合法的长度为i+j
的数组的数目;dp1[i][j]
是包含i
个0
和j
个1
且以1
结尾的合法的长度为i+j
的数组的数目。那么显然结果就是dp0[zero][one] + dp1[zero][one]
那么如何转移状态呢?
dp0[i][j]
数组是以0
结尾的,那么把这个0
还回去,找上一个状态。显然要从dp0[i-1][j]
和dp1[i-1][j]
来找,因为上一个数组必然是包含i-1
个0
和j
个1
的。
对于dp1[i-1][j]
,这是OK的,因为这时上一个数组的结尾是1
,当前数组结尾是0
,必然可以直接转移,全部加上。
对于dp0[i-1][j]
,上一个数组结尾为0
,那么要排除这种情况:前面已经连续出现了limit
个0
(包括上一个数组的结尾0
),那么在这一连串0
之前必然是一个1
(否则上一个数组就已经不合法了)。这种情况的数目是dp1[i-limit-1][j]
。其他的可以安全加上。
因此状态转移是这样的:
dp0[i][j] = dp1[i-1][j] + dp0[i-1][j];
if (i > limit) {
dp0[i][j] -= dp1[i-limit-1][j];
}
dp0[i][j] = (dp0[i][j] % MOD + MOD) % MOD;
那么dp1[i][j]
的转移,就是把i
和j
对调一下。
dp1[i][j] = dp0[i][j-1] + dp1[i][j-1];
if (j > limit) {
dp1[i][j] -= dp0[i][j-limit-1];
}
dp1[i][j] = (dp1[i][j] % MOD + MOD) % MOD;
然后考虑边界情况,刚开始时应该把dp0[i][0]
在i <= limit && i <= zero
时都置为1
,因为这时只有1种方法,就是全0
,并且合法。对于dp1[0][j]
同理。
至于dp0[0][j]
和dp1[i][0]
在任何时候都是不合法的,因为违背了定义要以0
或者1
结尾,这些置0
即可。
完整代码
class Solution {
public:
int numberOfStableArrays(int zero, int one, int limit) {
const int MOD = 1000000007;
vector<vector<int>> dp0(zero+1, vector<int>(one+1, 0));
vector<vector<int>> dp1(zero+1, vector<int>(one+1, 0));
for (int i = 0; i <= min(limit, zero); i++)
dp0[i][0] = 1;
for (int j = 0; j <= min(limit, one); j++)
dp1[0][j] = 1;
for (int i = 1; i <= zero; i++) {
for (int j = 1; j <= one; j++) {
dp0[i][j] = dp1[i-1][j] + dp0[i-1][j];
if (i > limit) {
dp0[i][j] -= dp1[i-limit-1][j];
}
dp0[i][j] = (dp0[i][j] % MOD + MOD) % MOD;
dp1[i][j] = dp0[i][j-1] + dp1[i][j-1];
if (j > limit) {
dp1[i][j] -= dp0[i][j-limit-1];
}
dp1[i][j] = (dp1[i][j] % MOD + MOD) % MOD;
}
}
return (dp0[zero][one] + dp1[zero][one]) % MOD;
}
};