#include using namespace std; const int N = 110; const int M = 1 << 10; int n, m; // n行m列,n<=100,m<=10,注意:状态压缩DP对列的长度很敏感,太大不行 int g[N]; // 记录每一列的山地情况,是一个二进制标识转化后的十进制数,最大就是全都是1,也就是2^m-1 int cnt[M]; int f[N][M][M]; vector st; vector head[M]; // 检查一个状态是不是合法状态[同一行必须隔两个及两个以上才是安全的] bool check(int x) { return !(x & x >> 1 || x & x >> 2); } // 统计某个状态中数字1的数量 int count(int s) { int res = 0; for (int i = 0; i < m; i++) // 遍历每一列 if (s >> i & 1) res++; // 如果此个位置数字是1,那么总的数量++ return res; } int main() { // 输入 scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) // n行 for (int j = 1; j <= m; j++) { // m列 char c; scanf(" %c", &c); // 注意scanf输入char的时候,前面要加一个空格 if (c == 'H') g[i] += 1 << (j - 1); // 山地上不能够部署炮兵部队 } // 1、预处理所有合法状态[单行合法即可] for (int i = 0; i < 1 << m; i++) if (check(i)) st.push_back(i), cnt[i] = count(i); // 预算出每个状态i里有多少个数位是1 // 2、找出所有合法状态的合法转移[行间状态兼容检测] for (int a : st) for (int b : st) if (!(a & b)) // 上下行间不能存在同列的1 head[a].push_back(b); // 3、dp for (int i = 1; i <= n; i++) // 枚举每一行 for (int a : st) { // 枚举i行的每个状态 if (g[i] & a) continue; // 按i这样摆,确实是本行内不冲突,但如果准备放大炮的位置是高地,是不行的。 for (int b : head[a]) // 枚举第i-1行的每个可能状态,b肯定与a不冲突 for (int c : head[b]) // 枚举第i-2行的每个可能状态,c肯定是与b不冲突的 if (!(a & c)) // 如果c也与a不冲突的话,才是三行间完全兼容的方案 f[i][a][b] = max(f[i][a][b], f[i - 1][b][c] + cnt[a]); } // 4、枚举最终的状态最大值 int res = 0; for (int a : st) for (int b : head[a]) res = max(res, f[n][a][b]); // 5、输出 printf("%d\n", res); return 0; }