悬关!
AC on 1,2,9,11,12
WA on 3,4,5,6,7,8,10
思路:
预处理出每行里,满足行的条件的炮兵布置方案。
然后状压 DP,fi,j,k 表示第 i 行状态为 j,第 i−1 行状态为 k 的前 i 行能布置的最多炮兵数。
转移时,枚举第 i−2 行状态 l,判断 i&j,i&k,j&k 是否均 0。
代码里有详细注释:
#include<bits/stdc++.h>
using namespace std;
int n,m;
char mp[105][10];
int cp[105][105],ct[105];
int f[105][105][105];
int cm(int i,int j){
for(int k=0;k<m;k++){
if(((j>>k)&1)&&mp[i][k]=='H') return 1; //山里不能放炮兵
}
return 0;
}
int popcount(int x){
int res=0;
for(;x;x&=(x-1)) res++;
return res;
} //统计 x 里 1 的个数
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",mp[i]);
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<m);j++){
if(!((j&(j>>1))||(j&(j>>2))||cm(i,j))) cp[i][ct[i]++]=j; //预处理第 i 行满足条件的炮兵放置方案
}
}
cp[0][ct[0]++]=0; //第 0 行的特殊待遇
for(int j=0;j<ct[1];j++) f[1][cp[1][j]][0]=popcount(cp[1][j]); //DP 边界
for(int i=2;i<=n;i++){
for(int j=0;j<ct[i];j++){
for(int k=0;k<ct[i-1];k++){
if(cp[i][j]&cp[i-1][k]) continue;
for(int l=0;l<ct[i-2];l++){
if((cp[i][j]&cp[i-2][l])||(cp[i-1][k]&cp[i-2][l])) continue;
f[i][cp[i][j]][cp[i-1][k]]=max(f[i][cp[i][j]][cp[i-1][k]],f[i-1][cp[i-1][k]][cp[i-2][l]]+popcount(cp[i][j])); //DP 转移
}
}
}
}
int ans=0;
for(int j=0;j<ct[n];j++) for(int k=0;k<ct[n-1];k++){
if(!(cp[n][j]&cp[n-1][k])) ans=max(ans,f[n][cp[n][j]][cp[n-1][k]]); //百里挑一
}
printf("%d",ans); //输出答案
return 0;
}