POJ 2411 Mondriaan's Dream

来源:互联网 发布:河北工程大学网络 编辑:程序博客网 时间:2024/06/10 02:45

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

Mondriaan's Dream
Time Limit: 3000MS Memory Limit: 65536KTotal 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 21 31 42 22 32 42 114 110 0

Sample Output

10123514451205
题目大意:给你一个h*w的长方形,叫你用1*2的小长方形填满它,问共有几种填法。

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

                                                                             1

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

0011

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

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

可以想象,第一行的合法状态满足这样一个性质,如果出现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 longconst 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;}







1111
1111
0 0
原创粉丝点击