炮兵阵地题解
炮兵阵地题解
状压DP的恶(mu)心(ban)题,
我们可以发现题目中:m<=10
无疑,肯定是这一维表状态,
题目中,若在一点布置炮兵,则左右四点不能布置(可以预处理粗来)
但问题是上下四点也不能布置,
所以在DP时我们需要枚举上上行,上一行,这一行的状态,
dp数组也要用两维表示这一行和上一行的状态,
但如果用三维数组则会爆空间,用循环数组省一省。
#include<bits/stdc++.h>
using namespace std;
const int N=106,M=1026;
int n,m,o1=2,o2=0,o3=1,ans=-1,t,p,q,w,g[N],h[M],num[N],flag[N][M],dp[4][M][M];
char c[14];
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
int main(){
n=read(),m=read();
for(int i=0;i<(1<<m);++i){
p=i,q=0;
while(p) q+=(p&1),p>>=1;
h[i]=q;
}//预处理出每一种状态有几个炮兵
for(int i=1;i<=n;++i){
scanf("%s",c);
for(int j=0;j<m;++j) g[i]+=((c[m-j-1]=='P')<<j);
//g[],求出平原上的所有可能状态
for(int j=0;j<=g[i];++j)
if(((j&g[i])==j)&&(!((j<<1)&j))&&(!((j<<2)&j))) flag[i][++num[i]]=j;
//flag[i][]:第i行,满足不互相打到的所有状态
}
memset(dp,-1,sizeof(dp));
//dp[i][j][k]:第i行,这一行状态为j,上一行状态为k,第一位用循环数组省空间
for(int i=1;i<=num[1];++i) dp[0][flag[1][i]][0]=h[flag[1][i]];
for(int i=1;i<=num[1];++i){
p=flag[1][i];
for(int j=1;j<=num[2];++j){
q=flag[2][j];
if(p&q) continue;
dp[1][q][p]=max(dp[1][q][p],dp[0][p][0]+h[q]);
}
}
//处理第一二行的dp初值(p&q代表同一列有炮兵)
for(int i=3;i<=n;++i){
t=o1,o1=o2,o2=o3,o3=t;
for(int j=1;j<=num[i-2];++j){
p=flag[i-2][j];
for(int k=1;k<=num[i-1];++k){
q=flag[i-1][k];
for(int ww=1;ww<=num[i];++ww){
w=flag[i][ww];
if((p&q)||(p&w)||(q&w)) continue;//同一列有炮兵就跳过
dp[o3][w][q]=max(dp[o3][w][q],dp[o2][q][p]+h[w]);
}
}
}
}
for(int i=1;i<=num[n-1];++i){
p=flag[n-1][i];
for(int j=1;j<=num[n];++j){
q=flag[n][j];
if(p&q) continue;
ans=max(ans,dp[o3][q][p]);
}
}
printf("%d\n",ans);
return 0;
}