很久就看推荐题目有这个了,一直没做,因为看了好几次没看懂,都说dp,这几天看了状态压缩后明白了,其实就是用二进制来表示各个位置的状态
然后进行枚举,把状态放进数组里就行,在这里用dp[i][j][k]表示第i行,当前j状态,i-1行是k状态时候的最大炮数
dp[i][j][k]=MAX(dp[i][j][k],dp[i-1][k][p]+sum[j])
#include <stdio.h>
#include <string.h>
#include <iostream>
#define MAX(a,b) (a)>(b)?(a):(b)
using namespace std;
int dp[105][65][65],ant[105],n,m,k,map[105],sum[105];
bool ok(int x)
{
if(x&(x<<1))return false;//判断一行中,是否有两个1间隔小于2
if(x&(x<<2))return false;
return true;//没有的话是一个合法的状态
}
int getsum(int x)//此种状态下有多少个1
{
int num=0;
while(x>0)
{
if(x&1)num++;
x>>=1;
}
return num;
}
void find()
{
memset(ant,0,sizeof(ant));
for(int i=0;i<(1<<m);i++)
{
if(ok(i))
{
ant[k]=i;
sum[k++]=getsum(i);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(dp,-1,sizeof(dp));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
char tmp;
cin>>tmp;
if(tmp=='H')map[i]=map[i]|(1<<j);//把第i行原始状态取反后放入map[i]
}
}
k=0;
find();
for(int i=0;i<k;i++)//初始化第一行状态,特殊考虑
if(!(ant[i]&map[0]))
dp[0][i][0]=sum[i];
for(int r=1;r<n;r++)
{
for(int i=0;i<k;i++)//第r行的状态
{
if(map[r]&ant[i])continue;
for(int p=0;p<k;p++)//枚举第r-1行状态
{
if(ant[p]&ant[i])continue;//r与r-1没有想接触的
for(int q=0;q<k;q++)//枚举第r-2行
{
if(ant[i]&ant[q])continue;//r与r-2行没有接触的
if(dp[r-1][p][q]==-1)continue;
dp[r][i][p]=MAX(dp[r][i][p],dp[r-1][p][q]+sum[i]);
}
}
}
}
int ans=0;
for(int i=0;i<k;i++)
for(int j=0;j<k;j++)
ans=MAX(ans,dp[n-1][i][j]);
printf("%d\n",ans);
}
}

浙公网安备 33010602011771号