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; }

 

你可能感兴趣的:(代码)