【动态规划——状态压缩】dream——蒙德里安的梦

用二进制状态压缩,用f[i][s]表示做到第i行状态为s。

s的二进制表示第几位(第几个位置)是否被放过——状态s=10(1010)第一个位置被放过,第二个位置没被放过,第三个位置被放过,第四个位置没被放过。

然后分层枚举本层状态与上层状态,若两种状态相符合,f[i][s]+=f[i-1][ss](ss为上层状态,s为本层状态)。

判断状态符合方法:

1、s中j位为0,ss中j位为1(在i位竖着放,放在i-1行和i行)

2、s中j位为0,ss中j位为0(状态不符合)

3、s中j位为1,ss中j位为0(不放,之后在i+1行更新)

4、s中j位为1,ss中j位为1;s中j+1位为1,ss中j+1位为1(横着放)

5、s中j位为1,ss中j位为1;s中j+1位不为1,或ss中j+1位不为1(状态不符合)

在此之前提前处理第一行,只能横着放。

#include<cstdio>

#include<cstring>
int n,m;
long long f[21][5001];
int maxs=0;
bool check(int s,int ss)
{
for(int i=0;i<m;)
{
if(s&(1<<i))
{
if(ss&(1<<i))
{
if(i==(m-1)||(s&(1<<(i+1)))==0||(ss&(1<<(i+1)))==0)
{
return 0;
}
else
{
i+=2;
}
}
else
{
i++;
}
}
else
{
if(ss&(1<<i))
{
i++;
}
else
{
return 0;
}
}
}
return 1;
}
void solve()
{
for(int i=2;i<=n;i++)
{
for(int syh=0;syh<=maxs;syh++)
{
for(int zyh=0;zyh<=maxs;zyh++)
{
if(check(syh,zyh))
{
f[i][zyh]+=f[i-1][syh];
}
}
}
}
}
void cl1(int s,int u)
{
int cl;
if(s==maxs)
{
return;
}
for(int i=u;i<m;i++)
{
cl=s;
cl|=(1<<i);
cl|=(1<<(i+1));
f[1][cl]=1;
cl1(cl,i+2);
}
}
int main()
{
// freopen("dream.in","r",stdin);
// freopen("dream.out","w",stdout);
memset(f,0,sizeof(f));
scanf("%d %d",&n,&m);
if(n%2==1&&m%2==1)
{
printf("0");
return 0;
}
int ji=1;
for(int i=0;i<m;i++)
{
maxs+=ji;
ji*=2;
}
f[1][0]=1;
cl1(0,0);
solve();
printf("%lld",f[n][maxs]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41958857/article/details/80010630