题解/算法 {292. 炮兵阵地}
LINK: https://www.acwing.com/problem/content/294/
;
经典的二维矩阵的状压DP, 即以每个行为单位, 将一整行压缩为一个2进制状态;
-{ 错误做法;
(i,j): 第i行, j为第(i,i-1)行的状态 进行完(或运算)后的结果
, 令row为第i+1
行的状态, 那么 虽然通过j & row
确实可以判定 当前row
是否是合法的 即是否可以与j
来递推下面的状态, 但是 你无法得到 新状态到底是多少, 即(i+1, jj)
这个jj
是多少 你是得不到的, 因为你不知道第i
行的状态;
-}
因此, 需要同时记录i, i-1
的状态, 即(i, j, k): i行, i行的状态为j, (i-1)行状态为k
;
由于状态有1<<N = 1000
, 那么这显然状态太多了, 因此 先预处理所有合法状态 (提前打表可以发现, 最多只有60
个), 即(M=100, 60, 60)
;
注意点: 不要把DP初始化为0
, 因为 这里求的 不是方案数, 而是最大放棋子的个数, 那么你设置为0, 会有歧义的;
.
比如(i,j,k)
这个状态是非法的, 但他的值为0; 而假如一个状态是合法的 他的值 可能也是0, 这就歧义了;
代码
void __Solve(){
cin>> M>> N;
A.resize( M);
for( int i = 0; i < M; ++i){
string s; cin>> s;
int st = 0;
for( int j = 0; j < N; ++j){
if( s[ j] == 'H'){
st |= (1 << j);
}
}
A[ i] = st;
}
{
ValidSTs.clear();
for( int st = 0; st < (1 << N); ++st){
if( (st & (st >> 1)) != 0) continue;
if( (st & (st >> 2)) != 0) continue;
ValidSTs.push_back( st);
}
}
if( M == 1){
int ANS = 0;
for( auto st : ValidSTs){
if( (st & A[ 0]) != 0) continue;
MAX_SELF_( ANS, __builtin_popcount( st));
}
cout<< ANS<< ED_;
return;
}
{ // DP
//> DP( 1, ?)
memset( DP[ 1], -1, sizeof( DP[ 1]));
for( int cur = 0; cur < (int)ValidSTs.size(); ++cur){
for( int pre = 0; pre < (int)ValidSTs.size(); ++pre){
if( (ValidSTs[ cur] & ValidSTs[ pre]) != 0) continue;
if( (ValidSTs[ cur] & A[ 1]) != 0) continue;
if( (ValidSTs[ pre] & A[ 0]) != 0) continue;
//> 不要写成: `__builtin_popcount( ValidSTs[ cur] & ValidSTs[ pre])`
DP[ 1][ cur][ pre] = __builtin_popcount( ValidSTs[ cur]) + __builtin_popcount( ValidSTs[ pre]);
}
}
//> DP( >1, ?)
for( int row = 2; row < M; ++row){
memset( DP[ row], -1, sizeof( DP[ row]));
for( int cur = 0; cur < (int)ValidSTs.size(); ++cur){
if( (ValidSTs[ cur] & A[ row]) != 0) continue;
for( int pre = 0; pre < (int)ValidSTs.size(); ++pre){
if( (ValidSTs[ cur] & ValidSTs[ pre]) != 0) continue;
for( int pp = 0; pp < (int)ValidSTs.size(); ++pp){
if( (ValidSTs[ cur] & ValidSTs[ pp]) != 0) continue;
if( DP[ row - 1][ pre][ pp] == -1) continue;
auto & curDP = DP[ row][ cur][ pre];
MAX_SELF_( curDP, DP[ row - 1][ pre][ pp] + __builtin_popcount( ValidSTs[ cur]));
}
}
}
}
} // DP
int ANS = 0;
for( int cur = 0; cur < (int)ValidSTs.size(); ++cur){
for( int pre = 0; pre < (int)ValidSTs.size(); ++pre){
MAX_SELF_( ANS, DP[ M - 1][ cur][ pre]);
}
}
cout<< ANS<< ED_;
}