您的位置:首页 > 编程语言

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

2017-04-27 19:27 561 查看
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]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  状态压缩 dp