NC51189 Mondriaan's Dream
题目链接
关键点:
1、我们用上1,下0表示竖着放置的方块,用双0表示横着放置的方块,这样一行的状态就可以利用01来表示
2、接下来用f[i][j],表示状态为i,第j行的总共可以放置的方块,那么因为竖着的方块可以影响到两行的放置,所以我们在枚举时要考虑当前行和上一行的状态
3、对于当前行的状态和上一行的状态能否转移,我们可以发现,对于偶数列,我们可以用偶数个横方块,或者偶数个竖方块,和偶数个横方块,那么对于奇数列,我们可以用奇数个竖方块和偶数个横方块,这三种情况下,上下两行的或运算出来的连续的0,一定是为偶数个的,所以对于奇数个连续的0,我们要将其排除
4、初始化,对于第0状态的第一行,我们初始化为1,对于每一次枚举的当前行的状态都初始化为0
5、最终的最后一行要是刚刚好填满的,则最后一行应均为0
完整代码
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[(1<<12)+10][12];//竖着放为1,底下为0, 横着放两格为0
int h, w;
int judge(int x)//连续0个数为奇数返回1
{
int cnt = 0;
int ww = w;
while (ww--)
{
if (x&1)
{
if (cnt&1)
return 1;
cnt = 0;
}
else
cnt++;
x>>=1;
}
if (cnt&1)
return 1;
else
return 0;
}
int main()
{
while (scanf("%d%d", &h, &w))
{
if (h == 0 && w == 0)
break;
else
{
f[0][0] = 1;
for (int i=1; i<=h; i++)
{
for (int j=0; j<(1<<w); j++)//枚举当前行状态
{
if ((i==h)&&(j)) break;
f[j][i] = 0;
for (int k=0; k<(1<<w); k++)//枚举上一行
{
if ((i==1)&&(k)) break;
if (j&k) continue;
if (judge(j|k)) continue;
f[j][i] += f[k][i-1];
}
}
}
cout<<f[0][h]<<endl;
}
}
return 0;
}