思路:状态压缩DP。很经典的状态压缩DP。用int型来表示每行的状态(如果int型的二进制的第i位为1,则表示这一行的第i列有安装大炮)。这样的话由于最多有10列,故由计算可得最多有60种状态。DP部分:dp[r][i][k]表示第r行的状态为k,第r-1行的状态为i时候,前r行最多能够安装的大炮数量。
#include<stdio.h>
#include<string.h>
int dp[105][100][100];//dp[i][j][k]: “第i行状态是s[j],第i-1行状态是s[k]”的,数组开小点,会超内存
int s[100];//一行的状态选择s[0], s[1], ... , s[k-1]
int sum[100];
int map[105];//'H''P'地图map[0]~map[n-1],地图每一行map[line]: 1001 表示HPPH
char str[105][20];
int jude(int i)
{//状态s[x]是否造成行冲突
if(i&(i>>1))
return 1;
if(i&(i>>2))
return 1;
return 0;
}
int max(int a,int b)
{
return a>b?a:b;
}
/*因为最大列数不大于10,故可用dp进行状态压缩
注:所谓状态压缩即如:PHPP 可以用二进制0100表示,用十进制存储为4。
本题因其反向对称性,为了方便压缩,故上边实例压缩成0010,用2表示,不影响求解。*/
int main()
{
int n,m,ans,i,j,k,r,q,p,num,x;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(map,0,sizeof(map));
for(i=0;i<n;i++)
{
scanf("%s",str[i]);
for(j=0;j<m;j++)
if(str[i][j]=='H')
map[i]=map[i]|(1<<j);//把第i行原始状态取反后放入map[i]
}
k=0;
for(i=0;i<(1<<m);i++)
{
if(jude(i)==0)
{
s[k]=i;
num=0;
x=i;
while(x>0)
{//状态s[x]下有多少个1
if(x&1)
num++;
x>>=1;
}
sum[k++]=num;
}
}
memset(dp,0,sizeof(dp));
// 初始化第0行状态
for(i=0;i<k;i++)
if(!(s[i]&map[0]))//s[i]为1的位如果对应平原(0),则&运算后为0
dp[0][i][0]=sum[i];
// 计算第1~n-1行状态
for(r=1;r<n;r++)
{
for(i=0;i<k;i++)//枚举第r行的状态 s[i]
{
if(map[r]&s[i]) continue;//通过地形排除部分第r行的状态
for(p=0;p<k;p++)//枚举第r-1行状态 s[p]
{
if(s[i]&s[p]) continue; //r与r-1没有想接触的
for(q=0;q<k;q++)//枚举第r-2行状态s[q]
{
if(s[p]&s[q]) continue;
if(s[i]&s[q]) continue;
dp[r][i][p]=max(dp[r-1][p][q]+sum[i],dp[r][i][p]);
}
}
}
}
ans=0;
for(i=0;i<k;i++)
for(j=0;j<k;j++)
ans=max(ans,dp[n-1][i][j]);
printf("%d\n",ans);
}
return 0;
}