和上一题有点类似。不过对于当前的第i行状态,除了牵涉到第i-1行的状态外,还和第i-2行的状态有关。
考虑枚举前两行的状态,来进行状态转移:
设dp[i][s][s1]表示第i行状态为s、第i-1行状态为s1时,能安置最多炮兵的数量。
分别枚举第i-1和i-2行的状态s1、s2,有:
dp[i][s][s1]=max(dp[i][s][s1],dp[i−1][s1][s2])
对于该题,每行状态最多有2^10,显然直接枚举时间和空间上都是行不通的。
注意到,由于多个限制条件的存在,其实很多状态都是不合法的。于是可以把这些不合法的状态预先筛掉,将合法的状态用一个数组保存下来。这样,在枚举的时候直接枚举合法的状态即可。对于空间上,发现对于每一个行而言,合法的状态(即满足同一行炮兵不相互攻击的状态)实际上只有不超过60个,因此可以考虑用状态的下标来表示状态,这样空间复杂度也得到了优化。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 10
typedef __int64 LL;
char M[100][maxn];
int dp[100][60][60];
int state[60],cur[101],num[60];
bool judge1(int i,int s)
{
if((cur[i]&s)!=s) return 0;
return 1;
}
bool judge2(int s1,int s2)
{
if(s1&s2) return 0;
return 1;
}
int getnum(int s)
{
int ans=0;
while(s) {
if(s&1) ++ans;
s>>=1;
}
return ans;
}
int main()
{
int i,j,k,p,n,m,S,cnt;
while(~scanf("%d%d",&n,&m))
{
getchar();
S=(1<<m);
cnt=0;
memset(dp,0,sizeof(dp));
for(j=0;j<S;++j)
if((!(j&(j>>1)))&&(!(j&(j>>2)))) {
num[cnt]=getnum(j);
state[cnt++]=j;
}
char c;
for(i=0;i<n;++i){
cur[i]=0;
for(j=0;j<m;++j)
{
cin>>c;
cur[i]=(cur[i]<<1)+(c=='P');
}
}
for(i=0;i<cnt;++i)
if(judge1(0,state[i])) dp[0][i][0]=num[i];
for(i=0;i<cnt;++i){
if(!judge1(1,state[i])) continue;
for(j=0;j<cnt;++j)
if(judge1(0,state[j])&&judge2(state[i],state[j])) dp[1][i][j]=max(dp[1][i][j],dp[0][j][0]+num[i]);
}
for(i=2;i<n;++i)
for(j=0;j<cnt;++j)
{
if(!judge1(i-2,state[j])) continue;
for(k=0;k<cnt;++k)
{
if(!judge1(i-1,state[k])) continue;
for(p=0;p<cnt;++p)
if(judge1(i,state[p])&&judge2(state[p],state[j])&&judge2(state[p],state[k]))
dp[i][p][k]=max(dp[i][p][k],dp[i-1][k][j]+num[p]);
}
}
int ans=0;
for(i=0;i<cnt;++i){
if(!judge1(n-1,state[i])) continue;
for(j=0;j<cnt;++j) ans=max(ans,dp[n-1][i][j]);
}
printf("%d\n",ans);
}
return 0;
}