1. 程式人生 > >【動態規劃——狀態壓縮】dream——蒙德里安的夢

【動態規劃——狀態壓縮】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;
}