先看同一行哪一些方案可以放置,以减小时间复杂度(老套路)
查看的方法是看左边第一、二,右边第一、二 不能跟自己这个位置同时有1

然后统计这个方法包含的炮兵数,统计方法是线性dp,通过lowbit(树状数组的东西),转移计数。(每个方案的炮兵数量是这个方案的二进制串去除最低位的1的炮兵数量+1)
最后一行一行向前dp,记住原则,一直统计新的一行的平原,并用二进制串表示,再相邻行不断的判断筛选掉不可行的方案,用dp数组向前推进可行方案,每推进一次更新一次ans的最大值,最终输出ans的最大值即可。。

#include <iostream>
using namespace std;
char map[105][12];
int method[2020],num[2020]; int op[105];
int cnt=0;
int dp[105][500][500];
int lowbit(int x)
{
    return x&(-x);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf(" %c",&map[i][j]);
    for(int i=0;i<(1<<m);i++)//枚举各个方案,选择能够放置的
    {
        if(i&(i>>1)) continue;
        if(i&(i>>2)) continue;
        if(i&(i<<1)) continue;
        if(i&(i<<2)) continue;
        method[++cnt]=i;
    }
    for(int i=1;i<(1<<m);i++)
    {
        num[i]=num[i-lowbit(i)]+1;
    }
    int ans=0;
    for(int i=1;i<=n;i++)//一行一行往前dp
    {
        for(int j=1;j<=m;j++)
        {
            op[i]|=((map[i][j]=='H')<<(j-1));
        }
        for(int j=1;j<=cnt;j++)
        {
            if(method[j]&op[i]) continue;//存在放置到山地的方案
            for(int k=1;k<=cnt;k++)
            {
                if(method[k]&method[j]) continue;//i-1行与i行冲突
                int v=num[method[j]];//存放i行新增的方案数
                if(i>=2&&(method[k]&op[i-1])) continue;//i-1行放到了山地上
                for(int p=1;p<=cnt;p++)
                {
                    if(method[p] & op[i - 2]) continue;
                    if((method[p] & method[k]) == 0 && (method[j] & method[p]) == 0)   
                    {
                        dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k][p] + v);
                        ans=max(ans,dp[i][j][k]);
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
}