poj 2411 狀壓dp

Mondriaan's Dream

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 23134   Accepted: 12887

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

輸入n,m時,我們要把列儘量縮小。因爲我們以行爲基準,對每一列進行多次遍歷判斷。

動態規劃的無後效性正好符合本題。我們只有豎直襬放纔會影響到下一行,所以每次我們只需關注上一行就行了。

具體在註釋中講解

 

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#define  LL long long
#define  ULL unsigned long long
#define mod 10007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
//#define maxn 50
using namespace std;
const int maxn = (1 << 11) + 5;
int mp[maxn][maxn];
int n,m;
LL dp[maxn][maxn];
bool init(int state)
{
    for(int j = 0; j < m;){
      if(state & (1 << j)){
        if(j == m - 1) return false;//因爲我們擺放是從左到右的,當前的方塊填1,那麼下一列的也一定要是1.所以j不能爲爲最後一列
        if(state & (1 << (j + 1))) j+=2;
        else return false;
      }
      else j++;//豎直襬放,當前爲0,可行
    }
    return true;
}
bool judge(int sn,int sp)
{
    for(int j = 0; j < m;){
      if(sn & (1 << j)){//本行j列爲1
        if(sp & (1 << j)){//上一行j列也爲一
            if(j == m - 1 || !(sn & (1 << (j + 1))) || !(sp & (1 << (j + 1)))) return false;//那麼我們可以得知 上一行和本行都是橫着擺放,都要爲1
            else j+=2;
        }
        else j++;//上一行爲0,豎直襬放,可行

      }
      else{
          if(sp & (1 << j)) j++;//本行j列爲0,豎直襬放,可行
          else return false;
        }
    }
    return true;
}
void solve()
{
    for(int s = 0; s < (1 << m); s++){//初始化第一行的所有狀態
       if(init(s)) dp[1][s] = 1;
    }
    for(int i = 2; i <= n; i++){//從第二行開始遍歷dp數組
      for(int j = 0; j < (1 << m); j++){//本列的狀態遍歷
        for(int k = 0; k < (1 << m); k++){//上一列的狀態遍歷
            if(judge(j,k)){
              dp[i][j] += dp[i - 1][k];//每一行的結果一定由上一行決定
            }
        }
      }
    }
    printf("%lld\n",dp[n][(1 << m) - 1]);
}
int main()
{
    while(~scanf("%d%d",&n,&m) && (n + m)){
      mem(dp,0);
      dp[1][0] = 1;
      if((n * m) % 2 != 0) {printf("0\n");continue;}
      if(n < m) swap(n,m);
      solve();
    }


    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章