题目:Mondriaan's Dream
链接:http://poj.org/problem?id=2411
题意:用 1*2 的瓷砖去填 n*m 的地板,问有多少种填法。
思路:
很久很久以前便做过的一道题目,状压DP,当时写得估计挺艰辛的,今天搜插头DP又搜到它,就先用状压DP写了下,顺利多了,没一会就出来了,可惜因为long long没有1A。
思路挺简单,一行一行解决,每一列用1 表示对下一行有影响,用0 表示对下一行没有影响,所以一行最多2048 种可能,然后要筛选一下,因为有些本身就不合理,有些因为上一行的影响变得不合理,然后简单的三重循环搞定,发现以前的代码效率更高,懒得追究了,一起贴出来。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #define N 200 5 typedef long long LL; 6 int ans[N],ao; 7 bool check(int i,int m) 8 { 9 int co=0,o=0; 10 while(i) 11 { 12 o++; 13 if(i&1) 14 { 15 if(co&1) return false; 16 else co=0; 17 } 18 else 19 { 20 co++; 21 } 22 i>>=1; 23 } 24 if((m-o)&1) 25 return false; 26 return true; 27 } 28 void find(int m) 29 { 30 for(int i=0;i<(1<<m);i++) 31 { 32 if(check(i,m)==1) 33 { 34 ans[ao++]=i; 35 } 36 } 37 } 38 void dis(int i,int m) 39 { 40 int o=0; 41 while(i) 42 { 43 printf("%d",i&1); 44 o++; 45 i>>=1; 46 } 47 for(int j=o;j<m;j++) 48 printf("0"); 49 printf("\n"); 50 } 51 int pre[12][410000],po; 52 LL dp[12][2050]; 53 bool check_2(int a,int b) 54 { 55 while(a) 56 { 57 if(a&1) 58 { 59 if(b&1); 60 else return false; 61 } 62 a>>=1; 63 b>>=1; 64 } 65 return true; 66 } 67 int main() 68 { 69 int n,m; 70 while(scanf("%d%d",&n,&m)!=EOF) 71 { 72 if(n==0&&m==0) break; 73 ao=0; 74 find(m); 75 memset(dp,0,sizeof(dp)); 76 po=0; 77 for(int i=0;i<ao;i++) 78 { 79 pre[0][po++]=ans[i]; 80 dp[0][ans[i]]=1; 81 } 82 int ko=0; 83 bool v[2050]={0}; 84 for(int i=1;i<n;i++) 85 { 86 memset(v,0,sizeof(v)); 87 for(int k=0;k<po;k++) 88 { 89 if(v[pre[i-1][k]]) continue; 90 v[pre[i-1][k]]=1; 91 for(int j=0;j<ao;j++) 92 { 93 if(check_2(pre[i-1][k],ans[j])) 94 { 95 //printf("pre %d ans %d\n",pre[i-1][k],ans[j]); 96 pre[i][ko++]=ans[j]^pre[i-1][k]; 97 dp[i][pre[i][ko-1]]+=dp[i-1][pre[i-1][k]]; 98 } 99 } 100 } 101 po=ko; 102 } 103 printf("%I64d\n",dp[n-1][0]); 104 } 105 return 0; 106 }