Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 783 Accepted Submission(s): 506
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
1 0 1 2 3 5 144 51205
解题思路:
如图:问铺满大举行一共有多少种方法。因为长宽最大11,可以状态压缩.
从第一行开始铺砖。
dp[ i ] [ j ] 定义为 第i行的状态为 j 一共有多少种方法 .
把小矩形用01状态表示,小矩形由两个正方形组成, 对于横着放的小矩形,左右两个正方形用11表示,对于竖着的小矩形,上下两个正方形用分别01表示。
第i行的状态s2与第i-1行的状态s1有关。
s1和s2满足两个条件:
1. s1 | s2 得到的数二进制每一位都是1 ,因为对于竖着放的 ,0|1肯定是1,横着放的都是11,相或也是11.
2. s1 & s2 得到的数连续的1是偶数个,注意0也是偶数。这个看图观察就可以了。
本题犯的错误:
1.
获取一个数x二进制的第i位是0或者1,用 if( x&(1<<i) ==1) 是不对的, 这句话的意思是,把x的二进制数除了第i位都设为0,第i位通过 &1,来判断是0或者1,但是得到的数不一定是1,是2的倍数,比如 0010 或者 0100.
2.
判断一个数x二进制的每一位是否等于1 ,假设有m位 , 直接用 if( x==1<<m)-1),不用每一位的判断,前者效率更高。
代码:
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <vector> #include <algorithm> #include <string.h> using namespace std; #define ll long long ll dp[12][1<<12];//dp[i][j]表示第i行状态为j有多少种方法 int n,m; bool ok(int s1,int s2) { int temp=s1|s2;//两个状态或运算每一位都必须是1 if(temp!=(1<<m)-1) return false; int cnt=0; temp=s1&s2;//两个状态且运算,必须连续的1都是偶数个 for(int i=0;i<m;i++) { if((temp&(1<<i)))//第i位是1 cnt++; else { if(cnt&1) return false; } } if(cnt&1) return false; return true; } void solve() { memset(dp,0,sizeof(dp)); int maxd=1<<m; for(int i=0;i<maxd;i++)//铺第一行 if(ok(maxd-1,i)) dp[1][i]++; for(int i=2;i<=n;i++)//铺第i行 { for(int j=0;j<maxd;j++) { for(int k=0;k<maxd;k++) if(ok(j,k)) dp[i][j]+=dp[i-1][k]; } } ll ans=0; ans+=dp[n][maxd-1];//最后一行肯定都是1 printf("%I64d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { if(!n||!m) break; if(n*m&1)//小方块的个数为奇数个,肯定不能铺满 { printf("0\n"); continue; } if(n<m) n=n^m,m=n^m,n=n^m; solve(); } return 0; }