The solution of「P2704 [NOI2001] 炮兵阵地」
\textup{[NOI2001] 炮兵阵地}
发现自己不会状压了,做点题。
\textup{\textup{Description}}
给定有山地的地图,放置攻击范围为一个大小为两格十字架的炮兵,不能放在山上,两方攻击范围可以重叠。
问最多能放置多少个互不攻击的炮兵。
\textup{\textup{Solution}}
注意到
又因为当前行的炮兵只会受到前两行的影响,于是有状态定义
这样我们要转移到下一行就十分方便了。
我们计算一下复杂度,发现空间似乎存不下,考虑优化。
因为每行内炮兵互不攻击的方案数是有限的,算出大致为
这样既可以节省复杂度,还可以解决行内合法的问题。
于是定义
实现上,我们将地图中山地也状压,通过位运算可以清晰地得出当前状态是否合法。
具体的,我们枚举出新的状态后,将原先行进行与运算,判断相邻行间列的合法性,再转移即可。
注意,要初始化第
时间复杂度:
\textup{Code}
const int MAXN = 105;
const int MAXM = 105;
int N, M;
int mp[MAXN];
int dp[MAXN][MAXM][MAXM];
int mask[MAXN], cnt;
bool chk( int x ){ return !( ( x & ( x << 1 ) ) || ( x & ( x << 2 ) ) ); }
void solve(){//预处理合法状态
for( int i = 0; i < ( 1 << M ); i ++ )
if( chk( i ) ) mask[++ cnt] = i;
}
int main(){
cin >> N >> M;
for( int i = 1; i <= N; i ++ ){
for( int j = 0; j < M; j ++ ){
char k;
cin >> k;
if( k == 'H' ) mp[i] |= ( 1 << j );
}
}
memset( dp, -1, sizeof dp );
solve();
for( int i = 1; i <= cnt; i ++ ){//初始化
if( !( mask[i] & mp[1] ) )
dp[1][i][0] = __builtin_popcount( mask[i] );
}
for( int h = 2; h <= N; h ++ )
for( int i = 1; i <= cnt; i ++ ){
if( mask[i] & mp[h] ) continue;
for( int j = 1; j <= cnt; j ++ ){
if( ( mask[i] & mask[j] ) || mask[j] & mp[h - 1] ) continue;
for( int k = 0; k <= cnt; k ++ ){
if( k == 0 && h == 2 && dp[h - 1][j][0] != -1 ){
dp[h][i][j] = max( dp[h][i][j], dp[h - 1][j][0] + __builtin_popcount( mask[i] ) );
}
if( h >= 3 && ! ( mask[i] & mask[k] ) && !( mp[h - 2] & mask[k] ) && dp[h - 1][j][k] != -1 )
dp[h][i][j] = max( dp[h][i][j], dp[h - 1][j][k] + __builtin_popcount( mask[i] ) );
}
}
}
int ans = -0x3f3f3f3f;
for( int i = 1; i <= cnt; i ++ ){
for( int j = 0; j <= cnt; j ++ ){
ans = max( ans, dp[N][i][j] );
}
}
cout << ans;
return 0;
}
\textup{Last}
审核管理员辛苦了,如果您有什么看不懂、我太弱了所以讲错了的地方,请您在评论区指出或找我,我会一定解答并且修改本题解。
如果您觉得本文写的还不错,那可以留个赞吗?
QWQ。
谢谢你看到这里~