poj2411Mondriaan's Dream【覆盖类状压dp】

Description

Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.

Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!

Input

The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.

Output

For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.

Sample Input

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

Sample Output

1
0
1
2
3
5
144
51205

今天晚上状态超级不好啊==本文参考:点击打开链接

题意:用1x2的方块密铺nxm的矩形,问方法数,一看题就想到了hdu2946骨牌铺方格,只不过那个题是只有两行,其实只有两行就足以找出规律:

1.假设已知Fibo(n),如果将第n+1块板竖着插入排头,会发现并没有多出组合,Fibo(n+1)=Fibo(n),插入其他位置的情况下,
如果第n+1个位置是竖板,等同于前面的情况。
2.只有在n和n+1的位置是横板,才能多出组合,多的组合数量为Fibo(n-1);
3.推出 Fibo(n+1)=Fibo(n)+Fibo(n-1);即为Fibo(n)=Fibo(n-1)+Fibo(n-2);

同理我们考虑不止两行的情况,假设枚举到了第i行,前i-2行已经密铺了,将每行的骨牌有无压缩至一个十进制的二进制对应位,这个so easy吧,ss,s分别表示上行i-1状态和本行i状态,就像其他的dp题一样,状态不是直接找的对应的来转化的,而是判断两个状态是否可以转化再转化,这个过程就是下面的solve。重点是ok()这个判断函数:

1.第i行的第j列为1,第i-1行的第j列为1,这样的话,说明第i行的第j列一定不是竖放而是横放否则会与第i-1行的第j列冲突
  所以马上紧接着判断第i行第j+1列,如果是1,那么满足横放的规则,同时也要第i-1行第j+1列也要为1,否则的话这个格子没办法填充,
  成立后向左移动两格
  不满足上述条件的,就是两个不兼容或者不合法的状态
2.第i行第j列为1,第i-1行第j列为0,那么说明第i行第j列应该竖放并填充第i-1行第j列,成立后向左移动一格
3.第i行第j列为0,说明不放方块,那么第i-1行第j列必须为1,否则没法填充这个格子。若第i-1行第j列也为0,不兼容不合法
  (至于第i行第j列这个格子空着干什么,其实就是留出来给第i+1行竖放的时候插进来的)
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 15
#define MAX (1<<11)+10
long long dp[N][MAX];
long long ans[N][N];
int n,m;
bool init(int s)
{
    for(int i=0;i<m;)
    {
        if(s&(1<<i))
        {
            if(i==m-1) return false;
            if(s&(1<<(i+1)))i+=2;
            else return false;
        }
        else i++;
    }
    return true;
}
bool ok(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)))||!(ss&(1<<(i+1))))return false;
                i+=2;
            }
            else i++;
        }
        else
        {
            if(ss&(1<<i))i++;
            else return false;
        }
    }
    return true;
}
void solve()
{
    if(n<m) swap(m,n);
    memset(dp,0,sizeof(dp));
    int maxn=(1<<m)-1;
    for(int i=0;i<=maxn;i++)
        if(init(i))
            dp[1][i]=1;
    for(int i=2;i<=n;i++)
        for(int s=0;s<=maxn;s++)
            for(int ss=0;ss<=maxn;ss++)
                if(ok(s,ss))
                    dp[i][s]+=dp[i-1][ss];
    printf("%I64d\n",dp[n][maxn]);
}
int main()
{
    //freopen("cin.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        if(m==0&&n==0) break;
        if(m*n%2)
        {
            printf("0\n");
            continue;
        }
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值