NYOJ_515_完全覆盖_剖析大神的代码
大神的解题报告:http://hi.baidu.com/newmyl/item/afc7cb0ef6ef5b7dbee97e07
1、当高度(h)和宽度(w)为奇数时:
area=h*w;
骨牌面积:2
h*w / 2!=0 -> 不能用骨牌覆盖
2、记f[i][s1]为第i-1行全满且第i行状态为s1时的种数,f[i-1][s2]为第i-2行全满且第i-1行状态为s2时的种数,则骨牌的覆盖方案数会等于f[h][w<<1-1](第h行全满状态):
结论:第i行的放置方案数,取决于第i-1行的放置方案数
对于每一个位置,3种放置方案:
1. 竖直放置
2. 水平放置
3. 不放置
d为当前列号 ,初始化d, s1, s2都为0;对应以上三种放置方法,s1, s2的调整为:
1. d = d + 1, s1 << 1 | 1, s2 << 1;
2. d = d + 2, s1 << 2 | 3, s2 << 2 | 3;
3. d = d + 1, s1 << 1, s2 << 1 | 1;
先就第一种情况解释一下,另外的两种情况可以类推
S1<<1|1即为把s1的二进制表示后面加上一个1,对就于本题来说就是(d+1)列上放
置,s2<<1即为把s2的二进制表示后面加上一个0,对于本题来说就是(d+1)列上不放置。
但为什么s1、s2会如此变化呢?s1对应于本行的状态,s2对应于上一行的状态,能竖直放置意味着上一行的(d+1)列是空着的,因此此时上一行的状态为s2<<1,同时竖置放置了之后,则本行(d+1)行放置了东西,状态于是变为s1<<1|1;
3、初始时的f值,可以假设第0行全满,第一行只有两种放法:
1. 水平放置 d = d + 2, s << 2 | 3;
2. 不放置 d = d + 1, s << 1;
# include <stdio.h> # include <string.h> long long a[2][3000]; int t,n,m; void dfs(int d,int mm) //d列号 { if(d==m) { a[0][mm]++;//使用了滚动数组,当然是a[0]咯 return; } if(d+1<=m) dfs(d+1,mm<<1); if(d+2<=m) dfs(d+2,mm<<2|3); } void DFS(int d,int mm,int nn)//mm对应于上一行状态,nn对应于下一行状态 { if(d==m) { a[t][nn]+=a[(t+1)%2][mm]; return; } if(d+1<=m) //判断防越界 { DFS(d+1,mm<<1,nn<<1|1); //第一种放置 DFS(d+1,mm<<1|1,nn<<1); //第三种放置 } if(d+2<=m) DFS(d+2,mm<<2|3,nn<<2|3); //第二种放置 } int main() { int i; while(scanf("%d%d",&n,&m)&&n&&m) { if((n*m)%2) { printf("0\n"); continue; } if(n>m) //当输入的 w > h 时,对调,因为横向的运算是指数级的, 而列向的是线性的. n^=m,m^=n,n^=m; //相当于swap(n,m) memset(a,0,sizeof(a)); dfs(0,0); //初始化第一行 for(i=2;i<=n;i++) { t=(i+1)%2; DFS(0,0,0); memset(a[(t+1)%2],0,sizeof(a[0])); } printf("%I64d\n",a[(n+1)%2][(1<<m)-1]); //过不了就用cout
} return 0; }