经典的状态DP啊,虽然也可以用最大点独立集来做。不严谨的话,这应该是我第一道状态DP题。
刚开始没有思路,不知道怎么设状态。看了网上一些思路,有点头绪了。
分析:对于第k行哪些点放炮兵使得1~k行炮兵数最多与第k-1,k-2行哪些点放炮兵有关,所以对于第k行需要前面两行的(k-1,k-2)的状态才能确定,
每个位置状态只有放与不放(0,1),所以想到用状态压缩dp[k][a][b]来表示,a表示第k行放炮兵的第a种状态,b表示第k-1行放炮兵的第b种状态。
为什么这样设状态?因为这样就可以进行状态转移了,如果设dp[k][a],则发现无法进行状态转移,此时常见的方法就是增加维度,即增加新的因素,更细致的描述状态。
dp[k][a][b]=max(dp[k][a][b],dp[k-1][b][p]+num[a]); 约束条件是 sta[a]&sta[b]==0, sta[a]&sta[p]==0(即k的第a种状态与k-1的第b种状态,k-2的第p种状态都不冲突。)
其中sta[a]是第k行所对应的第a个状态,num[a]为第a个状态对应的可以放炮兵的位置个数。
其他一些要说明的细节或者求法,在代码里。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1<<7
int N,M,ans;
int dp[111][MAX][MAX];
int sta[MAX],num[MAX],sum,presta[MAX],prenum,prepresta[MAX],preprenum;
char ch[111][15];
void dfs(int i,int j,int p,int s)
{
if(j>=M) {sta[++sum]=p;num[sum]=s;return ;}
if(ch[i][j]=='P') dfs(i,j+3,p|(1<<j),s+1);
dfs(i,j+1,p,s);
}
void DP()
{
int i,j,k,p;
prenum=preprenum=1;
for(k=1;k<=N;k++)
{
sum=0; //第k行对应的状态数。(即 有几个状态)
memset(num,0,sizeof(num)); //用来存放 每个状态中1的个数(即可以放炮兵的位置数)
dfs(k,0,0,0); //求第k行对应的状态,以及每个状态可以放炮兵的个数。
for(i=1;i<=sum;i++)
{
for(j=1;j<=prenum;j++)
{
for(p=1;p<=preprenum;p++)
{
if(sta[i]&presta[j]) continue;
if(sta[i]&prepresta[p]) continue;
dp[k][i][j]=max(dp[k][i][j],dp[k-1][j][p]+num[i]);
}
}
}
for(i=1;i<=prenum;i++) //把上一行给上上行
{
prepresta[i]=presta[i];
}
preprenum=prenum;
for(i=1;i<=sum;i++) //把当前行给上一行
{
presta[i]=sta[i];
}
prenum=sum;
}
ans=0;
for(i=1;i<=sum;i++)
for(j=1;j<=prenum;j++)
{
ans=max(ans,dp[N][i][j]);
}
}
int main()
{
int i,j;
scanf("%d%d",&N,&M);
getchar();
for(i=1;i<=N;i++)
{
gets(ch[i]);
}
DP();
printf("%d\n",ans);
return 0;
}