分析:
限制条件:
- 考虑上下 同一列两格内不能放 不能 (x>>i&1) && ((x>>(i+1)&1) || (x>>(i+2)&1))
- 考虑左右 同一行两格内不能放 要满足 (a&c|a&b|c&b)==0
- 只能在平原上面放
状态表示:已经摆放完前i-1,且第i-2行与第i-1行 第i行 都满足限制条件,且第i行的摆放状态为j,第i-1行摆放状态为k的最多炮兵数
递推方式: 0~1<<m
属性:max
状态计算/子集划分:子集应该是前面所有合法的集合
由于两格内不能连续摆放,所以要记录 i行 ,i-1行 , i-2 行 三行内的所有摆放状态,但是记录三行会超出空间,所以我们只需要记录i-1行和i行就可以了,i-2行可以在dp[i-1][j][k]中表示出来
因为最后还可能会超出空间,于是选择使用滚动数组的方法 dp[i&1]...
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N=3,M=(1<<10)+10;
int dp[N][M][M];
int n,m;
int g[110];
vector<int> state;
int cnt[M];
bool check(int x)
{
for(int i=0;i<m;i++)
if((x>>i&1))
if((x>>(i+1)&1)||(x>>(i+2)&1))
return false;
return true;
}
int count(int x)
{
int res=0;
for(int i=0;i<m;i++)
res+=x>>i&1;
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++)
{
char a;
cin>>a;
g[i]+=(a=='H')?1<<j:0;
}
dp[0][0][0]=0;
for(int i=0;i<1<<m;i++)
if(check(i))
state.push_back(i),cnt[i]=count(i);
for(int i=1;i<=n;i++)
for(int j=0;j<state.size();j++)
for(int k=0;k<state.size();k++)
{
int a=state[j],b=state[k];
for(auto c:state)
{
if(a&b|a&c|b&c) continue;
if((g[i]&a)||(g[i-1]&b)) continue;
dp[i&1][a][b]=max(dp[i&1][a][b],dp[(i-1)&1][b][c]+count(a));
}
}
int res=0;
for(auto a:state)
for(auto b:state)
res=max(res,dp[n&1][a][b]);
cout<<res;
return 0;
}