1 /*状态压缩就是把一系列用二进制表示的状态压缩成一个十进制数*/
2 #include<stdio.h>
3 #include<string.h>
4 #include<iostream>
5 using namespace std;
6 int s[65],dp[150][65][65],cont=0,sum[65],map[150];//m为10根据要求每行最多60中状态
7 bool ok(int x)
8 {
9 if(x&(x<<1)) return false;//间隔1的不满足
10 if(x&(x<<2)) return false;//间隔2的不满足
11 return true;
12 }
13 int getsum(int x)//判断x有几个1,即这种状态能放多少大炮
14 {
15 int sum=0;
16 while(x>0)
17 {
18 if(x&1) sum++;
19 x>>=1;
20 }
21 return sum;
22 }
23 void init(int n)//初始化所有满足的状态
24 {
25 int i;
26 for(i=0;i<(1<<n);i++)
27 if(ok(i))
28 {
29 s[cont]=i;//记录满足的状态,大炮占的位置为1,山地 位置为0;
30 sum[cont++]=getsum(i);//记录这种状态可以放多少门大炮
31 }
32 }
33 int max(int a,int b)
34 {
35 if(a>b) return a;
36 return b;
37 }
38 int main()
39 {
40 int i,j,n,m,k,r;
41
42 while(cin>>n>>m)
43 {
44 memset(dp,-1,sizeof(dp));
45 for(i=0;i<n;i++)
46 {
47 for(j=0;j<m;j++)
48 {
49 char c;
50 cin>>c;
51 //printf("%c ",c);
52 //if(j==m-1) getchar();
53 //a[i][j]=c;
54 if(c=='H') map[i]|=(1<<j);//记录实际的地形,有山的位置为1;
55 }
56 }
57 init(m);
58 for(i=0;i<cont;i++)//特别判断第一行;
59 if(!(s[i]&map[0])) //s[i]中大炮位置为1,map中山地位置为1,如果想与后的结果不为0说明大炮和山地位置相同,是不满足的,这行是筛选所有满足的状态中符合这个地形的
60 dp[0][0][i]=sum[i];//前面的0,0代表第0行的没有前面两行,所以第0行的
61 for(r=1;r<n;r++)
62 for(i=0;i<cont;i++)//枚举第r行
63 {
64 if(s[i]&map[r]) continue;//剪枝,如果状态j不满足地形
65 for(j=0;j<cont;j++)//枚举第r-1行
66 {
67 if(s[i]&s[j]) continue;//如果这两行的大炮在同一列
68 for(k=0;k<cont;k++)//枚举第r-2行
69 {
70 if(s[i]&s[k]) continue;//
71 if(dp[r-1][k][j]==-1) continue;//没这种状态存在
72 dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);
73 }
74 }
75 }
76
77 /*状态转移,从i到j,从k到j再从j到i,sum[i]是本行每种状态i的大炮个数,
78 j代表r-1行大炮的放法,i是本行的放法,因为这个关系是一层一层传下来的,
79 所以那么dp[r][j][i]代表的是从开始行到r行的最大炮个数,*/
80 int ans = 0;
81 for(i = 0; i < cont; i ++)
82 for(j = 0; j <cont; j ++)
83 ans = max(ans,dp[n-1][i][j]);
84 printf("%d\n",ans);
85 }
86 return 0;
87 }
88
89
90 /*
91 5 4
92 PHPP
93 PPHH
94 PPPP
95 PHPP
96 PHHP*/