刷题记录(NC51189 Mondriaan‘s Dream)

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值