题解/算法 {291. 蒙德里安的梦想}
LINK: https://www.acwing.com/problem/content/293/
;
(i,j): 覆盖[0...i]行, 且第`i`行的状态为`j` (比如, j中的第k位二进制为1, 则说明有一个`(i,k),(i+1,k)`的竖板;
op{ st: 行状态[0,(1<<M))}
SET(i,j): { [op0, op1, ..., opi]}
DP(i,j): SET(i,j)的大小;
void __Solve(){
int N, M;
auto Is_valid = [&]( int _st) -> bool{
int c = 0;
for( int i = 0; i < M; ++i){
if( (_st >> i) & 1){
if( c & 1) return false;
c = 0;
}
else ++ c;
}
if( c & 1) return false;
return true;
};
while( true){
cin>> N>> M;
if( N == 0 && M == 0) break;
//> (0, ?)
for( int st = 0; st < (1 << M); ++st){
auto & curDP = DP[ 0][ st];
if( Is_valid( st)) curDP = 1;
else curDP = 0;
}
//> (>0, ?)
for( int row = 1; row < N; ++row){
for( int st = 0; st < (1 << M); ++st){
auto & curDP = DP[ row][ st];
curDP = 0;
for( int pre_st = 0; pre_st < (1 << M); ++pre_st){
if( pre_st & st) continue;
if( false == Is_valid( pre_st | st)) continue;
curDP += DP[ row - 1][ pre_st];
}
}
}
cout<< DP[ N - 1][ 0] ED_;
}
}
错误
你可能会设置一个合法状态的集合, 里面每个状态 都满足 为0的连续子段 长度为偶数;
比如M=5
, 第一行肯定不会是st=00000
这个状态 (因为你放2个横板, 总是会空余一个, 而这个空余的 下一行是不管的),因此 这个状态 对于第一行 确实是非法的;
.
但你可能会认为 00000
这个状态 对其他所有行 都是非法的, 这是错误的… 因为这个空余位 可以来自于 上一行的 竖板 (第一行没有上一行, 可以其他行都有上一行的);