Z - Mondriaan's Dream POJ - 2411 _状态压缩(轮廓线dp)

Z - Mondriaan's Dream

 POJ - 2411 

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

题意:有1*2或2*1的长方块组成h*w的大方块 的方案有多少种

如上图所示,一个状态由w位(k4k3k2k1k0)的二进制数组成,ki=1表示ki的位置被方块占有,每次遍历一个点(i,j)作为当前状态(k3k2k1k0O)的最后一个值,对于各种存在的k4k3k2k1k0,这里定义dp[cur][k3k2k1k0O]表示在当前状态k4k3k2k1k0下的方案数,cur有0和1两个值,取反cur=cur^1,得到另外一个cur值,可以用cur^1表示上一组状态

(1)如果k4=0,则必须向上放,则dp[cur][k3k2k1k01]+=dp[cur^1][k4k3k2k1k0]    (此种方案下,k4变为1)

(2)如果k4=1且k0=0,则可以选择向左放,有dp[cur][k3k2k111]+=dp[cur^1][k4k3k2k1k0], 

或选择不放则dp[cur][k3k2k100]+=dp[cur^1][k4k3k2k1k0]

以O(1,1)为右下角的时候,在O位置只能选择不放,而初始化的开始条件是dp[cur^1][11111]=1,表示与此次对接的方案数为1

每次以O为右下角,保证O放完K4是1,而k3k2k1k0O是01二进制数,并且保存当前各个符合要求的01二进制(用十进制表示)下的状态的方案数,最后O到达最右下角的时候,取dp[cur][11111]即为总的方案数。

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
#define mod 100000000
typedef long long ll;
const int nmax=44;
const double esp = 1e-9;
const int N=12;
int n,m;
ll dp[2][1<<N];
void solve()
{
    int top=(1<<m)-1;
    memset(dp,0,sizeof(dp));
    dp[0][top]=1;
    int cur=0,state;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            cur^=1;
            memset(dp[cur],0,sizeof(dp[cur]));
            for(int k=0; k<=top; k++) //遍历上一组状态
            {
                if(i!=1&&!(k&(1<<(m-1))))   //往上放,当前不是第一行且上面那个一个(上一个状态第一位)为0
                {
                    state=(((k<<1)|1)&((1<<m)-1));
                    dp[cur][state]+=dp[cur^1][k];
                }
                if(j!=1&&(k&(1<<(m-1)))&&!(k&1))    //往左放,当前不是第1列且上面那个一个(上一个状态第一位)为1
                {
                    state=(((k<<1)|3)&((1<<m)-1));
                    dp[cur][state]+=dp[cur^1][k];
                }
                if(k&(1<<(m-1)))   //不放,上面那个一个(上一个状态第一位)为1
                {
                    state=((k<<1)&((1<<m)-1));
                    dp[cur][state]+=dp[cur^1][k];
                }
            }
        }
    }
    printf("%lld\n",dp[cur][top]);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        if(n<m)
        {
            int t=m;
            m=n;
            n=t;
        }
        solve();
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值