挺简单的,只要理解了状态dp大概这道题就不算难,只是考虑的情况要多一点
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
int Map[N];
int dp[N][65][65];//dp[i][j][k]表示放第i行时,第i行为第j个状态,第i-1行为第k个状态最多可以放多少个炮兵
int s[N],num[N];
int n,m,p;
bool check(int x) { //判断本行的炮兵是否互相攻击
if(x&(x<<1)) return false;
if(x&(x<<2)) return false;
return true;
}
int Count(int x)//算出这行有多少个炮
{
int i=1,ans=0;
while(i<=x)
{
if(x&i) ans++;
i<<=1;
}
return ans;
}
void Init()//先列举所有行不冲突的情况,不考虑列是否冲突
{
p=0;
memset(s,0,sizeof(s));
memset(num,0,sizeof(num));
for(int i=0;i<(1<<m);i++){
if(check(i))
{
s[p]=i;
num[p++]=Count(i);
}
}
}
int main() {
char ch;
cin>>n>>m;
memset(dp,0,sizeof(dp));
memset(Map,0,sizeof(Map));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>ch;
if(ch=='H')
Map[i]+=(1<<(m-1-j));//P为0,H为1
}
}
Init();
for(int i=0;i<p;i++) //求第一行最多放多少
if(!(Map[0]&s[i]))// 不在山上
dp[0][i][0]=num[i];
for(int i=0;i<p;i++)//前两行最多放多少
{
if(Map[1]&s[i])//不与第一行冲突
continue;
for(int j=0;j<p;j++)
{
if((!(s[i]&s[j])))//一二行不冲突
dp[1][i][j]=max(dp[1][i][j],dp[0][j][0]+num[i]);
}
}
for(int r=2;r<n;r++)//地图的行数开始枚举
{
for(int i=0;i<p;i++)//当前行的状态
{
if(s[i]&Map[r])//在山上
continue;
for(int j=0;j<p;j++) //上一行的状态
{
if(s[j]&Map[r-1])//在山上
continue;
if(s[i]&s[j])//上一行与现在的行起冲突
continue;
for(int k=0;k<p;k++)//上上一行的状态
{
if(s[k]&Map[r-2])//在山上
continue;
if(s[j]&s[k]) //上上行与上一行冲突
continue;
if(s[i]&s[k])//上上行与当前行冲突
continue;
dp[r][i][j]=max(dp[r][i][j],dp[r-1][j][k]+num[i]);
}
}
}
}
int ans=0;
for(int i=0;i<p;i++)//枚举每一种合法情况的数量
{
for(int j=0;j<p;j++)
{
if(ans<dp[n-1][i][j])
ans=dp[n-1][i][j];
}
}
cout<<ans<<endl;
return 0;
}