这道题目和前面的题目很像,不过条件改为了放置大炮的格子的四个方向两格内不能出现其余大炮,因此DP的时候不仅仅是由上一层的情况推下来,还需要每一层的上一层,及上面两层的情况。所以要开一个三维数组来记录DP。分别表示第i行取法的二进制,第i-行取法的二进制以及第i行。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstdlib>
using namespace std;
int n,m;
int s[111];
int bin[111];
int top;
int num[111];
int dp[70][70][111];
bool jud1(int x)//4个方向临近两格不能出现大炮
{
if(x&(x<<1))
{
return false;
}
if(x&(x<<2))
{
return false;
}
return true;
}
bool jud2(int x,int i)//是否有位置冲突
{
if(x&bin[i])
{
return false;
}
return true;
}
int cnt(int x)//统计每种取法的大炮数目,节省时间
{
int num=0;
while(x)
{
num++;
x&=(x-1);
}
return num;
}
void init()
{
char a;
bin[0]=0;
for(int i=1;i<=n;i++)
{
getchar();
for(int j=0;j<m;j++)
{
a=getchar();
if(a=='H')
{
bin[i]|=(1<<(m-j-1));//每一行地图的二进制表示
}
}
}
top=0;
memset(dp,-1,sizeof(dp));
for(int i=0;i<(1<<m);i++)//剪枝枚举各种状态
{
if(jud1(i))
{
s[top++]=i;
}
}
}
void solve()
{
init();
for(int i=0;i<top;i++)
{
num[i]=cnt(s[i]);//记录每种取法的大炮数目
if(jud2(s[i],1))
{
dp[i][0][1]=num[i];//基础状态
}
}
for(int i=2;i<=n;i++)//从第2行开始
{
for(int j=0;j<top;j++)//枚举一行每种状态
{
if(!jud2(s[j],i))
{
continue;
}
for(int k=0;k<top;k++)//上一行的各种状态
{
if(s[k]&s[j])//有上下相邻的为1的不可取
{
continue;
}
for(int l=0;l<top;l++)//从上一行的各种状态推过来
{
if(s[l]&s[j])//距离为2
{
continue;
}
if(dp[k][l][i-1]==-1)//上一状态不能取就跳过
{
continue;
}
dp[j][k][i]=max(dp[j][k][i],dp[k][l][i-1]+num[j]);//从上一行各种状态推过的来的最大值
}
}
}
}
int ans=0;
for(int i=0;i<top;i++)
{
for(int j=0;j<top;j++)
{
ans=max(dp[i][j][n],ans);//推到第N行的各种状态最大的数
}
}
printf("%d\n",ans);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
solve();
}
return 0;
}