poj2411(状压dp)
Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 22150 | Accepted: 12398 |
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!
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
Source
这题一开始用爆搜,发现最大就只能跑7*10的,再大答案都出不来了…………。
后来发现是状压dp,索性就看了看怎么写的。
直接上篇博客吧,毕竟感觉自己也写不出这么详细(嫌麻烦!!)
博客的方法交了后发现视乎有点慢,后来又发现另一种方法,要快很多,但是没有详细解释,所以我打算讲讲这个代码。
直接看代码吧,解释都写注释里了。
1 #include<cstdio> 2 #include<string.h> 3 #include<iostream> 4 using namespace std; 5 typedef long long ll; 6 int N,M; 7 ll allsta; 8 ll dp[12][(1<<12)];///dp[i][j]=k第i行的第j状态下有k种方法 9 bool init[(1<<12)];///这里是个优化,处理出一行中的所有状态是否合法 10 bool check(ll a,ll b){ 11 if((a|b)!=allsta-1) return false;///(a|b如果不等于tt-1就意味着两行有上下两个格子都为0,而这种情况是不可能的) 12 ll k=(a&b);///(a&b是两行的这个状态下的上下都为1后的状态是否合法,至于为什么可以这样,是因为init中存着一行中所有的情况是否合法) 13 return init[k]; 14 } 15 void Init(){ 16 memset(init,true,sizeof(init));///初始化全部合法 17 for(ll i=0;i<(1<<11);i++){ 18 bool flag=false; 19 ll x=i; 20 while(x){///这个模拟一下就知道了 21 ///因为只有横着贴/竖着贴,所以如果横着贴,那它和它下一位都必须为1, 22 if(x&1){ 23 flag=!flag;///这个操作保证了它和它下一位必须为1 24 }else if(flag){///上一位为1,这一位为0,则这个状态不合法(横着贴) 25 init[i]=false; 26 break; 27 } 28 x>>=1; 29 } 30 if(flag) init[i]=false;///这个是最后一位不能为1(即不可能贴出外面去) 31 } 32 } 33 int main(){ 34 Init(); 35 while(scanf("%d%d",&N,&M)!=EOF&&N&&M){ 36 if(N*M%2){ 37 printf("0\n"); 38 continue; 39 } 40 memset(dp,0,sizeof(dp)); 41 if(N<M) swap(N,M);///优化,让状态数减少 42 allsta=(1<<M); 43 for(int i=0;i<allsta;i++){ 44 if(init[i]){ 45 dp[0][i]=1; 46 } 47 } 48 for(int i=1;i<N;i++){ 49 for(ll j=0;j<allsta;j++){ 50 for(ll k=0;k<allsta;k++){ 51 if(check(j,k)){///状态转移 52 dp[i][j]+=dp[i-1][k]; 53 } 54 } 55 } 56 } 57 printf("%lld\n",dp[N-1][allsta-1]); 58 } 59 return 0; 60 }