Live2d Test Env

POJ2411Mondriaan's Dream(DP+状态压缩 or 插头DP)

 

问题:

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

 

 

  • 到底是这么想到用01和11的,是不是有一类这样处理的题型呢?
  • 这种题一般都有很多优化,包括很多预处理;或者运算方式的优化,比如尽量用位运算;或者有多个check时优先check哪一个。
  • 这里实现了两种情况避免了暴力check(据说还有很多优化,但是代码肯定没我现在这么短了,嘻嘻哒):

               α,上(x)下(y)不能同时为0,则一定是x|y==(1<<m)-1。这样就不需要一位一位check

               β,上(x)下(y)同为1的连续个数需要为偶,即z=x&y中1必须成连续偶数次出现(表示竖着放的部分)。我们预先处理各种z,避免了每次都check。

  •  这里的写法应该是归类为插头DP还是轮廓线DP还是其他?不清楚,我已经混了,或者本来就是交叉没有明确界限的。还可以用轮廓DP的思想来解决,过几天再试一试。

 

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
long long  dp[1<<12][12];int n,m;
bool ok[1<<12];
void getok()
{
    for(int i=0;i<(1<<11);i++){
        ok[i]=true;
         for(int j=0;j<11;j++)
           if(i&1<<j){
             int cnt=1;
             while(i&(1<<(j+1))) cnt++,j++;
             if(cnt&1){ok[i]=false ;break;}
         }
    }
}
bool check(int x,int y)
{
    if((x|y)!=((1<<m)-1)) return false; //上下不能同时为0 
    return ok[x&y];//上下同时为1要满足连续时为偶。 
}
int main()
{
    int i,j,k;
    getok();
    while(~scanf("%d%d",&n,&m)){
        if(n==0&&m==0) return 0;
        memset(dp,0,sizeof(dp));
        dp[(1<<m)-1][0]=1;
        for(i=1;i<=n;i++)
          for(k=0;k<(1<<m);k++)
            for(j=0;j<(1<<m);j++)     
              if(check(k,j)) dp[j][i]+=dp[k][i-1];
        printf("%lld\n",dp[(1<<m)-1][n]);
    }
    return 0;
}

 

posted @ 2017-12-03 19:51  nimphy  阅读(578)  评论(0编辑  收藏  举报