思路:
这题题目和poj 3254放牛非常相似,区别在于除了前一行,这题还受到上上一行的影响。
一开始我选择dp[i][j]表示第i行当前行状态为j的时候的最大方案数,然后暴力枚举前两行的状态直接加起来
这样做的错误在于线性转移的时候会重复计算,证明我的阶段划分出错了,所以后来改为dp[i][j][k]表示枚举到第i个,第i个状态是j,上一个是k,这样转移过来就不会有重复计算 ,其余的和 poj 3254 没什么区别,预处理出同一行可行的情况存下来,并把每个状态的1的个数预处理出来
这题debug了3h,还有一个问题是我的初始化,一开始初始化为负数,转移过来的时候切记要从被记录过的状态转移过来,除此以外初始化dp[1][i][k]的时候 要看清下面循环的时候第一个被用到的状态是哪个,在我的范围内就是dp[1][i][1]
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[105][105][105],sta[1<<10+2],mp[105],num[1<<10];
char s[105][12];
bool adj(int x)
{
if(((x&(x<<1))==0)&&((x&(x<<2))==0))
return 1;
return 0;
}
bool is(int i,int j)
{
return (mp[i]&sta[j]);
}
int cal(int x)
{
int cnt=0;
for(;x;x-=x&-x)
cnt++;
return cnt;
}
int main()
{
int n,m,cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(s[i][j]=='H')mp[i]+=(1<<j-1);
for(int i=0;i<1<<m;++i)
{
if(adj(i))
sta[++cnt]=i,num[cnt]=cal(i);
}
memset(dp,-1,sizeof(dp));
int ans=0;
for(int i=1;i<=cnt;++i)
{//初始化的时候要看清下面的
if(is(1,i)==0)
dp[1][i][1]=num[i];
ans=max(ans,dp[1][i][1]);
}
for(int i=2;i<=n;++i)
{
for(int j=1;j<=cnt;++j)
{
if(is(i,j))continue; //自己不能违背
for(int k=1;k<=cnt;++k)
{
if((is(i-1,k)==0)&&((sta[j]&sta[k])==0)) //自己和上一个
{
int last=0;
for(int f=1;f<=cnt;++f)
{//自己和上一个 自己和上上个 只有符合状态的才能转移过来
if((is(i-2,f)==0)&&(dp[i-1][k][f]!=-1)&&(sta[j]&sta[f])==0)
last=max(last,dp[i-1][k][f]);
}
dp[i][j][k]=max(dp[i][j][k],last+num[j]);
if(i==n)
ans=max(ans,dp[i][j][k]);
}
}
}
}
printf("%d\n",ans);
return 0;
}