本题的行和列的范围相差很多,因此很容易想到要用状态压缩。对于(i,j)位置,影响它能否放炮兵的因素有两个:1.是否是平地 2.(i-1,j)或(i-2,j)处是否放有炮兵。至于与它同行的就不用考虑了,因为在设计算法是可以很容易的避免同行的相互攻击。所以我们必须要记录i-1行和i-2行的放置炮兵的情况,首先很容易想到把第i-1行和i-2行分别按二进制位压缩,但很快就把这种想法给否定了,因为这样做的话所占空间为 n*1024^2(n<=150),这个数字相当大了。再仔细思考后发现(i-1,j),(i-2,j)这两个位置的炮兵只有三种情况,要么都没放,要么放了一个,所以就想着用三进制位压缩了,0,1,2分别表示着三种情况。然后程序实现就比较简单了,下面贴代码。
//g++提交
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[150][60000];
char map[150][12];
//power数组依次存储3^0,3^1,……
int power[10]={1,3,9,27,81,243,729,2187,6561,19683};
int n,m;
//获取第pos位上的数字
int getbit(int i,int pos)
{
if(pos==0)
return i%3;
if(pos>=m)
return 0;
if(i>=power[pos])
return (i/power[pos])%3;
return 0;
}
//x,y为横纵坐标,cur为上两行的状态,next为下一状态,cnt记录第x行已放置的炮兵
void dfs(int x,int y,int cur,int next,int cnt)
{
if(y==0)//刚进入当前行
{
if(dp[x][cur]!=-1)
return;
dp[x][cur]=0;
}
if(y>=m)//已到行尾
{
if(x<n-1)//转到下一行
{
dfs(x+1,0,next,0,0);
dp[x][cur]=max(dp[x][cur],cnt+dp[x+1][next]);
}
else//已到最后一行
dp[x][cur]=max(dp[x][cur],cnt);
return;
}
//下面主要注意状态的转移部分了,一定要细心
int i=getbit(cur,y);
if(i==0&&map[x][y]=='P')//(x,y)处可以放置炮兵
{
int j=2*power[y],k;
k=getbit(cur,y+1);
if(k==2)
j+=power[y+1];
k=getbit(cur,y+2);
if(k==2)
j+=power[y+2];
dfs(x,y+3,cur,next+j,cnt+1);
dfs(x,y+1,cur,next,cnt);
return;
}
//不能放置炮兵
if(i==2)
dfs(x,y+1,cur,next+power[y],cnt);
else
dfs(x,y+1,cur,next,cnt);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=0;i<n;i++)
scanf("%s",map[i]);
memset(dp,-1,sizeof(dp));
dfs(0,0,0,0,0);
printf("%d\n",dp[0][0]);
}
return 0;
}