【分析】
本来想的和周伟nei胖子差不多…但是感觉时间会炸,于是又苟且看了讲解…发现胖子果然用会炸的方法…但是毕竟十几年前的题了…数据略弱(。・・)ノ
dp[i][j][k]表示前i行中,第i行状态为j,第i-1行状态为k时炮兵的最多数量(状态均为01的二进制串)。方程很好写: dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[j]),num[j]代表状态j的炮兵数量,比如num[10101]=3。把jkl都枚举一下就行(虽然时间上趋近于炸)。
至于预处理,留给读者思考吧。(先找出所有不合法的相邻与隔一个相邻的状态标记,然后再处理合法状态中占有山地的状态…具体用暴力实现。复杂度一千W)
【代码】
//NOI 2001 廉渊升的阵地(炮兵阵地)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
int n,m,p,ans;
bool b[1<<10],can[105][12],ok[105][1<<10];
int num[1<<10],dp[2][1<<10][1<<10]; //前i行,且第i行为状态j时,第i-1行为k时 的最多人数
inline void init()
{
int ttt[15],change[1<<11];
int i,j,k,s;
char c;
scanf("%d%d",&n,&m);
fo(i,1,n) fo(j,1,m) {cin>>c;if(c=='P') can[i][j]=1;}
fo(i,0,10) change[1<<i]=i+1;
fo(s,0,(1<<m)-1)
{
int now=s,next=0,c=0;
while(now)
{
int tmp=now&-now;
if(tmp==next*2 || tmp==next*4) break;
now-=tmp;
next=tmp;
ttt[++c]=change[tmp];
}
if(!now)
{
b[s]=1,num[s]=c;
fo(i,1,n)
{
bool flag=1;
fo(j,1,m) if(!can[i][j])
{
if(!flag) break;
fo(k,1,c)
if(ttt[k]==j)
{flag=0;break;}
}
if(flag) ok[i][s]=1;
}
}
}
}
int main()
{
int i,j,k,s,now,pre,l;
init();
now=1;
fo(j,0,(1<<m)-1) if(b[j] && ok[1][j]) dp[1][j][0]=num[j];
fo(i,2,n)
{
now=i&1,pre=now^1;
memset(dp[now],0,sizeof dp[now]);
fo(j,0,(1<<m)-1) if(b[j] && ok[i][j])
fo(k,0,(1<<m)-1) if(b[k] && ok[i-1][k] && (j&k)==0)
fo(l,0,(1<<m)-1) if(b[l] && (i==2||ok[i-2][l]) && (j&l)==0)
dp[now][j][k]=max(dp[now][j][k],dp[pre][k][l]+num[j]);
}
fo(j,0,(1<<m)-1)
fo(k,0,(1<<m)-1)
ans=max(ans,dp[now][j][k]);
printf("%d\n",ans);
return 0;
}
//5 4
//PHPP
//PPHH
//PPPP
//PHPP
//PHHP