POJ 2411 Mondriaan's Dream

题目链接:http://poj.org/problem?id=2411

Mondriaan's Dream
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 12131 Accepted: 7075

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
题目大意:给你一个h*w的长方形,叫你用1*2的小长方形填满它,问共有几种填法。

算法:状压DP,用横放用1  1表示,竖放用 0 表示

                                                                             1

例如一个2*2的长方形可以这样填充

11
11


00
11

即两个小长方形都横放和都竖放。

每一行用一个数的二进制表示一个状态

可以想象,第一行的合法状态满足这样一个性质,如果出现1,则连续1的个数必定为偶数个,否则该状态不合法。例如0000是合法的,0001就不合法。0011合法。

然后判断上下两行的状态是否相容即这两行是否被小长方形填满,设它们分别为x,y如果(x|y)!=(1<<m)-1的话,说明它们的某一位都为0,即出现竖着的00,这显然是不合法的。

满足了上一个条件之后,再者就是还有判断它们横放是否合法,就是判断(x&y)是否是合法状态,这个简单,因为我们已经预处理过了。

接下来就是状态转移,当前行的状态j,等于它上一行所有与它相容状态的和,即dp[j][i]=sum(dp[k][i-1]);

下面是AC代码:

#include<cstdio>
#include<cstring>
#define LL long long
const int maxn=1<<11;
int Begin[maxn],n,m;
LL dp[maxn][12];
bool is(int x)
{
    int cnt=0;
    while(x)
    {
        if(x&1) cnt++;
        else if(cnt&1) return 0;//此状态有连续奇数个1则状态不合法
        x>>=1;
    }
    return !(cnt&1);//此状态有连续奇数个1则状态不合法,否则合法
}
void init()
{
    memset(Begin,0,sizeof(Begin));
    for(int i=0;i<(1<<11);i++)//找出所有合法状态
        if(is(i)) Begin[i]=1;
}
bool judge(int x,int y)
{
    if((x|y)!=(1<<m)-1) return 0;//如果个状态有某位同时出现0即竖着出现0,0,则这两个状态不兼容
    return Begin[x&y];//看横放的木棍是否合法
}
int main()
{
    init();
    while(~scanf("%d%d",&n,&m)&&(n+m))
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<(1<<m);i++) dp[i][0]=Begin[i];//初始化第一行
        for(int i=1;i<n;i++)//从第二行开始递推
            for(int j=0;j<(1<<m);j++)//枚举本行所有状态
                for(int k=0;k<(1<<m);k++)//枚举上一行所有状态
                    if(judge(k,j)) dp[j][i]+=dp[k][i-1];//合法则加起来
        printf("%lld\n",dp[(1<<m)-1][n-1]);
    }
    return 0;
}







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值