很久就看推荐题目有这个了,一直没做,因为看了好几次没看懂,都说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); } }