转:瓷砖问题

这个题在POJ上有,地址是:http://acm.pku.edu.cn/JudgeOnline/problem?id=2411
以前做过这个题,是用状态DP来做的。大致说下方法,DP需要用到两维,一维表示到达哪一行,另一维表示这一行的状态。

用二进制来表示
假定方格为N行M列。
表示方法如下:
f[i][j]。 ---- i表示第i行,j表示该行的二进制状态。而f[i][j]则记录由i,j构成的状态有多少种摆法。

我举个例子,假定总共有4行5列。
如下状态: *号表示被铺了方块,0表示尚未被铺方块。
*****
*0**0
00000
00000
可以看到第2行的状态为*0**0,即用二进制来表示就是10110,即 f[2][22] 来表示。(当然,行号也可以从0开始计)

以上是状态表示方法,有了状态表示方法以后就需要得到一个进行状态转移的递推式。
考虑i行j列的那个格子,有3种状态,第一种是由第i-1行j列铺上来,第二种是 在本行内,与相邻的格子铺的瓷砖,第三种该格子留空,以备下一行来使用。
枚举这一行的格子的每一种状态,即可以得到所需的上一行状态。

举例来说,枚举一个如下状态(*表示该格子是已经被填充的,-表示空格子,其余数字均表示2*1的瓷砖摆法):

1***3**
1-22344

可知本行的状态为 1011111,生成该状态所需要的上一行状态为,0111011。因为只有这样才能满足瓷砖1和瓷砖3的嵌入且又不会留下空格子。

因此可以由本状态进行一次累加:用二进制表示第二维 即 a[i][1011111] += a[i-1][0111011]。

那么,最后 a[N][(1<<M)- 1]即为所得。

#include <stdio.h>
#include <string.h>

long long s[14][14][5000];
int w,h;
int list[20],v[20];

void GetList(int step){
    if (step>w){
        int value1=0;
        int value2=0;
        int i;
        for (i=1;i<=w;i++){
            value1<<=1;
            value2<<=1;
            switch (list[i]){
                case 0:
                    value1|=1;
                    break;
                case 1:
                    value1|=1;
                    value2|=1;
                    break;
                case 2:
                    value2|=1;
                    break;
            }
        }
        if (h>1)
            s[w][h][value2]+=s[w][h-1][value1];
        else
            s[w][h][value2]=1;
        return;
    }
    if (h>1) list[step]=2,GetList(step+1);
    if (step<w) list[step]=list[step+1]=1,GetList(step+2);
    list[step]=0;
    GetList(step+1);    
}


int main(){
    memset(s,0,sizeof s);
    int i,j,k;
    v[0]=1;
    for (i=1;i<20;i++)
        v[i]=v[i-1]<<1;
    for (w=1;w<=12;w++)
        for (h=1;h<=12;h++)
            GetList(1);
    for (scanf("%d%d",&w,&h);w;scanf("%d%d",&w,&h))
        printf("%I64d\n",s[w][h][v[w]-1]);
}

 

posted @ 2013-04-05 21:37  upon77  Views(185)  Comments(0)    收藏  举报