题目描述 https://loj.ac/problem/10173
1. 读入时转成两进制数,P为0,H为1(这样后面的时候方便判断状态是否合法)。m的范围为10,我们要同时枚举三行的状态,三层循环显然是超时的,所以我们需要预处理出可行的方案,其实才不到70种.同时用cal(i)记录状态i中有多少个1,即炮兵。
2.用 ans[i][j][k]表示第i行状态为k上一行状态为j时的方案数,枚举时注意判断三行中互不侵犯和本行是否可以这样放。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int ans[105][100][100],st[100],f[105],sum[100],cnt,an,tot;
int cal(int x)
{
int cm=0;
while(x!=0)
{
cm+=(x&1);
x/=2;
}
return cm;
}
int main()
{ int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char a; cin>>a;
if(a=='H') f[i]|=(1<<(j-1));//H为1 P为0
}
cnt=(1<<m)-1;//ans[i][j][k]表示第i行状态为k上一行状态为j
for(int i=0;i<=cnt;i++)
if(((i&(i<<1))==0)&&((i&(i<<2))==0))
st[++tot]=i,sum[tot]=cal(i);//cal(i)为状态i中有多少个1
for(int i=1;i<=tot;i++)
if((st[i]&f[1])==0) ans[1][1][i]=sum[i];
for(int i=1;i<n;i++)//枚举行
for(int j=1;j<=tot;j++)//枚举上一行状态
for(int k=1;k<=tot;k++)//枚举本行状态
if((st[j]&st[k])==0)//合法
for(int p=1;p<=tot;p++)//枚举下一行状态
if(((st[j]&st[p])==0)&&((st[k]&st[p])==0)&&((st[p]&f[i+1])==0))
ans[i+1][k][p]=max(ans[i+1][k][p],ans[i][j][k]+sum[p]);
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++)
an=max(an,ans[n][i][j]);
printf("%d",an);
return 0;
}