1. 程式人生 > >Z - Mondriaan's Dream POJ - 2411 _狀態壓縮(輪廓線dp)

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;
}