状态压缩DP--蒙德里安的梦想

首先蒙德里安问题是求解所有横竖1x2小长方形的摆放情况个数,显然当我们摆放完横着的小长方形之后,竖着的小长方形的摆放情况唯一,即只需要求出摆放横小长方形的合法方案数即可。

那么,如何摆放横小长方形才能合法呢?我们可以把棋盘看成一张图,被占用的格子属性记为1,没被占用的则记为0。

那么想要让方案合理,显然每一列的连续的0必须是偶数个的,因为如果是奇数个的空白格子,1x2的竖小长方形是无法填充完的。这就是第一个预处理。

对于第二个预处理,我们必须使横着的1x2小长方形互相分离,不能有重叠部分,因此我们需要用到状态压缩DP中的状态函数来模拟所有横1x2小长方形的摆放情况,我们设f[a][b]意为前a-1列已经填充完毕情况下第a列的情况(将该情况记为b)所对应的方案数量。同时我们记k为j的前一列,第二个预处理就是保证j和k不会在同一行摆放横小长方形。

既然已经知道了合法方案的预处理判定,接下来只需要进行DP,状态更新然后输出即可

上代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int N=12,M=1<<12;
long long f[N][M];
bool st[M];
vector<int> states[M];
int m,n;
int main(){
    while(cin>>n>>m,n||m){
        //预处理1:预处理st,每列不能有奇数个0
        for(int i=0;i<1<<n;i++){
            int cnt=0; //记录连续个0
            bool flag=true;
            for(int j=0;j<n;j++){
                if((i>>j)&1){
                    if(cnt&1){ //判断两个1区间内的情况是否合法
                        flag=false;
                        break;
                    }
                    cnt=0;
                }
                else cnt++;
            }
            if(cnt&1) flag=false;
            st[i]=flag;
        }
        //预处理2:预处理states,找出所有合法情况
        for(int j=0;j<(1<<n);j++){
            states[j].clear();
            for(int k=0;k<(1<<n);k++)
                if((j&k)==0&&st[j|k])  //即j,k不重叠且0个数不为奇数
                    states[j].push_back(k); //意为当第i列情况为j时,第i-1列情况k的可行方案
        }
        //开始dp
        memset(f,0,sizeof f);
        f[0][0]=1;
        for(int i=1;i<=m;i++){
            for(int j=0;j<(1<<n);j++)
                for(auto k:states[j])
                    f[i][j]+=f[i-1][k];
        }
    cout<<f[m][0]<<endl;
    }
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初梦铅笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值