Mondriaan's Dream(状压dp)

Mondriaan's Dream(状压dp)

题目大意:用1x2的方块填满NxM的大矩形,问填满的方法有多少种。
解题思路:利用先填好竖着的方块,剩下的空格再用横着的来填,且要求填好竖着的方块时,每一行都要能用横着的方块填满(即连续的空出来的位置必须是偶数,即合法)

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#define ll long long
using namespace std;
const int maxn=12;
ll dp[maxn][1<<maxn];//后面的表示的是,这一行会向下一行突出的状态
bool ap[1<<maxn];
vector<int> st[1<<maxn];
int main(void)
{
    int N,M;
    while(~scanf("%d %d",&N,&M)&&(N||M))
    {
        if(N*M%2)//可以先排除一些,面积必须是偶数
        {
            puts("0");
            continue;
        }
        for(int i=0;i<(1<<M);i++)//处理每一行该有的状态,就是合法的状态,每一行空出来的都必须是偶数
        {
            int sum=0;
            for(int j=0;j<M;j++)
            {
                if(i&(1<<j))//遇到了没空的,检查空的是否为偶数
                {
                    if(sum&1)
                    {
                        ap[i]=false;
                        break;
                    }
                    sum=0;
                }else sum++;
            }
            if(sum&1)ap[i]=false;//要看看剩下来空的是否为偶数
            else ap[i]=true;
        }
        for(int j=0;j<(1<<M);j++)//预处理,由于题目有多组测试样例,可能会超时,所以要预处理
        {//每一行突出的状态可以更新出什么状态
            st[j].clear();//注意多组测试样例
            for(int k=0;k<(1<<M);k++)
            {
                if((j&k)||!ap[j|k])continue;
                st[j].push_back(k);
            }
        }
        memset(dp,0,sizeof(dp));//注意多组测试样例
        dp[0][0]=1;
        for(int i=1;i<=N;i++)
            for(int j=0;j<(1<<M);j++)
                for(int k=0;k<st[j].size();k++)
                    dp[i][j]+=dp[i-1][st[j][k]];
        cout<<dp[N][0]<<endl;//最后一行向后面一行突出的要为0
    }
    return 0;
}
posted @ 2022-09-11 21:48  WUTONGHUA02  阅读(26)  评论(0编辑  收藏  举报