#include<bits/stdc++.h>
using namespace std;
const int N=100+5,M=60+5;
char s[12];int e[N];
int A[M],tot,num[M],sta[N][M],dp[3][M][M];
inline int get_one(int x){//返回二进制的x里面有多少个1
int num=0;
while(x) ++num,x-=(x)&(-x);
return num;
}
//dp[i][b][a]=max(dp[i][b][a],dp[i-1][c][b]). 第i行状态为a 第i-1行状态为b 第i-2行状态为c
//(要求ab不冲突 bc ac不冲突)
int main(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
for(int j=1;j<=m;++j) e[i]+=(s[j]=='H')?(1<<(j-1)):0;
}
for(int i=0;i<(1<<m);++i){
if((i&(i>>1))==0&&(i&(i>>2))==0&&(i&(i<<1))==0&&(i&(i<<2))==0){//判断左1 左2 右1 右2是否同时有1
A[++tot]=i,num[tot]=get_one(i);//将一行不相互攻击的状态存起来
if((i&e[1])==0) dp[1][0][tot]=num[tot];
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=tot;++j)
if((A[j]&e[i])==0) sta[i][++sta[i][0]]=j;//将每行合法的状态的下标存起来
}
if(n==1){printf("%d\n",sta[1][0]);return 0;}
for(int sta0=1;sta0<=sta[2][0];++sta0)//第2行状态 A[a] 特判前两行
for(int sta1=1;sta1<=sta[1][0];++sta1){//第1行状态 A[b]
int a=sta[2][sta0],b=sta[1][sta1];//注意sta0 sta1不是状态的下标 sta[i][sta0]存储的才是下标
if((A[a]&A[b])==0) dp[2][b][a]=dp[1][0][b]+num[a];
}
for(int i=3;i<=n;++i)//dp
for(int sta0=1;sta0<=sta[i][0];++sta0)//当前行状态
for(int sta1=1;sta1<=sta[i-1][0];++sta1){//上一行状态
int a=sta[i][sta0],b=sta[i-1][sta1],c;
if(A[a]&A[b]) continue;//当前行与上一行冲突
for(int sta2=1;sta2<=sta[i-2][0];++sta2){//上上一行状态
c=sta[i-2][sta2];//当前行不与上一行冲突 上一行不与上上一行冲突
if((A[a]&A[c])==0&&(A[b]&A[c])==0) dp[i%3][b][a]=max(dp[i%3][b][a],dp[(i-1)%3][c][b]+num[a]);//滚动数组
}
}
int ans=0;
for(int sta0=1;sta0<=sta[n][0];++sta0)//第n行状态
for(int sta1=1;sta1<=sta[n-1][0];++sta1){//第n-1行状态
int a=sta[n][sta0],b=sta[n-1][sta1];
if((A[a]&A[b])==0) ans=max(ans,dp[n%3][b][a]);
}
printf("%d\n",ans);
}
P2704(炮兵阵地 状压dp)
最新推荐文章于 2022-07-02 14:53:57 发布