题解/算法 {3130. 找出所有稳定的二进制数组 II}
@LINK: https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-ii/
;
很容易想到一个DP: (i,j,k,l)
表示[0...i]
的序列 其中有j
个1
, 且最后有连续k
个l(0/1)
的合法序列(即不存在连续相同子段长度>L
)
.
DP转换的话, 如果k>1
那么等于(..., k-1, l)
; 否则当k==1
时 他等于(...,1|2|3|...|L, l^1)
之和;
可他的时间是N^3
的;
考虑把k
优化掉; 令(i,j,l)
表示[0...i]
的序列 其中有j
个1 且末尾为l
的 合法序列(即不存在连续相同子段长度>L
)
.
你可能认为, 我们肯定是需要记录末尾连续子段长度的呀, 否则怎么跟L
联系到一起呢? 别着急, 先这么定义 一步步来;
比如对于形如[..., p, 1]
的序列, 他可以继承[..., p=0]
这肯定没问题; 关键是 如果要继承[..., p=1]
(设其方案数为X), 他里面可能会包含 形如[..., (L个1)]
(假设其方案数为X1), 注意 不存在末尾有>L个1
的情况(因为DP定义里规定了方案的连续长度都是<=L
的);
因此 关键是求出X1, 这样X - X1
的方案 就是形如[..., p=1, 1]
的合法方案了;
.
这也不难, [..., (L个1)]
,他的方案数 等于[...]
里以0结尾的, 即形如[..., 0, (L个1)]
;
其实你只要想到 X - X1
, 即把非法的方案给去除掉,即在DP的前提下 再配合组合问题来优化, 这题就不难;
还有难点 就是很多边界判断/下标越界等 很容易出错, 参见代码把…
int numberOfStableArrays(int Z, int O, int L){
using Mod_ = ___Modulo_<int, int(1e9) + 7>;
static Mod_ DP[ 2003][ 1003][ 2];
FOR_( i, 0, Z+O-1){ FOR_( j, 0, O){ FOR_( k, 0, 1){ DP[i][j][k] = 0;}}}
DP[0][0][0] = 1;
DP[0][1][1] = 1;
FOR_( i, 1, Z+O-1){
FOR_( j, 0, O){ // `j`是枚举`[0...i]`里`1`的个数 (不是`[0...(i-1)]`里面的)
//> 这里很重要, 他保证了 你此时的`(i,j)`是合法的(即里面`1,0`的个数 都是合法的);
if( (i+1-j)<0 || (i+1-j)>Z){ continue;}
//> 注意要特判, 即保证`0`的个数是`>=1`的;
if( i+1-j >= 1){ // [...][0]
auto & curDP = DP[i][j][0];
curDP = 0;
curDP += DP[ i-1][j][1]; // [...1][0]
curDP += DP[ i-1][j][0]; // [...0][0] (他里面包含了非法方案 下面要去除掉)
int count = i+1 - j; // `[0...i]`中 `0`的个数;
if( count > L){ // 此时说明 `[...0][0]`里面 有非法方案 即`[... (L个0)][0]`;
ASSERTsystem_( i - L-1 >= -1); // 表示`...`的末尾元素下标;
if( i - L-1 == -1){ // 此时是`[L个0][0]`;
curDP -= 1;
}
else{
// curDP -= DP[ i - L-1][ j][0]; // 这是错误的;
curDP -= DP[ i - L-1][ j][1];
}
}
}
//> 注意要特判, 即保证`1`的个数是`>=1`的;
if( j >= 1){ // [...][1]
auto & curDP = DP[i][j][1];
curDP = 0;
curDP += DP[ i-1][ j-1][0]; // [...0][1] (注意是`j-1`)
curDP += DP[ i-1][ j-1][1]; // [...1][1] (注意是`j-1`)
int count = j; // `[0...i]`中 `1`的个数; (写成`count=j+1`是错误的)
if( count > L){ // 此时说明 `[...1][1]`里面 有非法方案 即`[... (L个1)][1]`;
ASSERTsystem_( i - L-1 >= -1); // 表示`...`的末尾元素下标;
if( i - L-1 == -1){ // 此时是`[L个1][1]`;
curDP -= 1;
}
else{
curDP -= DP[ i - L-1][ j - L-1][0];
// curDP -= DP[ i - L-1][ j - L-1][1]; // 这是错误的;
}
}
}
}
}
return (DP[ Z+O-1][ O][ 0] + DP[ Z+O-1][ O][ 1]).Value;
}