Mondriaan's Dream 瓷砖覆盖地板 编程之美

Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his ‘toilet series’ (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.

这里写图片描述

Expert as he was in this material, he saw at a glance that he’ll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won’t turn into a nightmare!

Input

The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.

Output

For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.

这里写图片描述

Sample Input
1 2
2 3
2 4
2 11
4 11
0 0

Sample Output
1
0
5
144
51205

题目
这个题目的题意很容易理解,在一个H*W的格子里,我们现在有两种类型的 砖块,1 * 2 和 2 * 1,问一共有多少种方案,可以将整个H*W的空间都填满。

题解:状态压缩与动态规划

铺砖过程:
一层一层的铺,每一层铺砖的方案有不确定个,即有不同的状态。
下一层可以怎样铺只与上一层有关,要用到到达上一层的方案数
用dp[i][j]将 到达第i层第j个状态 可用的方案数目记录下来, 第i+1层的方案数就可以根据上一层的方案数来确定

详细介绍:
空间为H*W:即每行有W个方格要填,当确定第i层时,每个方格可能铺或不铺

1,此方格不铺,由下一层竖着铺

  • 若此方格不铺,还必须满足上一层的方格铺了

2,此方格铺

  • 横着铺,必须满足上一层的方格铺了,且下一个方格也被铺了
  • 竖着铺,①上一层没铺,②上一层铺了

标记时:

  • 在位置(i, j) 如果我们选择横着铺,这个值其实对下一行如何选择没有影响,可以将(i, j), (i, j+1)都填写成1
  • 如果竖着铺,我们将(i,j)填写成0, 将(i+1, j)填写成1.
    (竖着贴砖的第二个,我们也选择了1, 因为这个砖头结束了,对下一行如何选择依然没有影响。而竖着的第一个砖头,这个砖头是对下面有影响的,如果(i,j)是0,那么(i+1, j)只有是1的情况下才能满足条件。)

状态压缩

在一个算法的过程中需要保存不同的状态数据(表示一种状态,例如 一某层铺砖的方案,方案有好多种) ,每个状态数据可以通过 2 进制来表示的。这就要求状态数据的每个单元只有两种状态,比如说棋盘上的格子,放棋子或者不放,或者是硬币的正反两面。这样用 0 或者 1 来表示状态数据的每个单元,而整个状态数据就是一个一串 0 和 1 组成的二进制数。

假设本题是3*4的方格:则某一层铺或不铺的方案为,只要相邻两层满足条件,每种方案都有可能取 ,最后一层只能取111,因为要填满

12345678
000001010011100101110111

0代表第状态j中的第i个方格不填,1代表第状态j中的第i个方格填,


#include<stdio.h>
#include<string.h>
#define Max_row 11
#define Max_status 2048

long long dp[Max_row][Max_status];
int H,W; 

int check(int j){
    int i=0;//j用二进制表示的第i个数 
    while(i < W){
        if((j & (0x1 << i))!=0){//从低位数起第i位(从第0位开始)是否为0
            if(i==W-1 || (j & (0x1 << (i+1)))==0){
                return 0;
            }
            i += 2;
        }
        else{
            i++;
        }
    }
    return 1; 
}

int Compare(int j,int k){
    int i=0;
    while( i < W ){
        if( (j & (0x1 << i)) == 0 ){//状态j的第i位为0时分析 
            if((k & (0x1 << i))==0){
                return 0;
            }
            i++; 
        }
        else{//状态j的第i位为1时分析 
            if((k & (0x1 << i))==0){
                i++;
            }
            else if( (i==W-1) || !( (j & (0x1 << (i+1))) && (k & (0x1 << (i+1))) ) ){
                return 0;
            }
            else i+=2;
        }
    }
    return 1;
}

int main(){
    int i,j,k,s;//s总状态数,i是第i行,j第i行的第j个状态,k第i-1行的第k个状态,s最大状态二进制表示全为1 
    scanf("%d%d",&H,&W);
    memset(dp,0,sizeof(dp));
    s=(2 << (W-1))-1;
    for(j=0;j<=s;j++){//第0行的s+1个状态,j==0表示此状态用二进制表示全为0 
        if(check(j)==1){
            dp[0][j]=1;
        }
    }   
    for(i=1;i<H;i++){//从第一行开始一行一行遍历 
        for(j=0;j<=s;j++){
            for(k=0;k<=s;k++){
                if(Compare(j, k)==1) { 
                    dp[i][j] += dp[i-1][k]; 
                }               
            }
        }
    }
    printf("%lld",dp[H-1][s]); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值