Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K |
Total Submissions: 16906 | Accepted: 9755 |
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
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
Sample Output
1
0
1
2
3
5
144
51205
假設n=5,m=5
1 | 1 | 1 | 1 | 1 |
1 | 1 | S4 | S3 | S2 |
S1 | S0 | X | ||
本題的狀态為d[i][j][S]表示 目前以[i,j]格為末尾的輪廓線狀态為<S3,S2,S1,S0,X>的值對應二進制表示時且S3之前的所有格子的值都為1時的方法總數
設目前為第[3,3]格,上一格為[3,2],則可以得到上一格的輪廓線狀态為:S=<s[2,3],s[2,4],s[2,5],s[2,1],s[2,2]> (s[i,j]∈{0,1})
這一格的輪廓線狀态為SS=<s[2,4],s[2,5],s[3,1],s[3,2],X>
那麼如何由S轉移到SS呢?
條件:
①如果第[i,j]格不放骨牌,那麼第[i-1,j]格一定要是被填充過
②如果第[i,j]格放向上的骨牌,i>1且第[i-1,j]格一定要是沒被填充過
③如果第[i,j]格放向左的骨牌,j>1第[i-1,j]格一定要是被填充過且[i,j-1]沒被填充過
④如果第[i,j]格放向下的骨牌,這種情況和[i+1,j]格放向上的骨牌相同,是以不用考慮
得到狀态轉移方程:
①S&(1<<(m-1))==1時,dp[i][j][S<<1]+=dp[i][j-1][S]
②i>1且S&(1<<(m-1))==0時,dp[i][j][S<<1|1]+=dp[i][j-1][S]
③j>1且S&1==0時,dp[i][j][S<<1|3]+=dp[i][j-1][S]
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MX = 1<<11;
int n,m,cur;
LL dp[2][MX];
void update(int S,int SS){
//隻有新的輪廓線最高位是1時才更新
if(SS&(1<<m)) dp[cur][SS^(1<<m)]+=dp[cur^1][S];
}
int main(){
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&m),n||m){
int mx=(1<<m)-1;
memset(dp,0,sizeof(dp));
cur=0;
dp[cur][mx]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cur^=1;
memset(dp[cur],0,sizeof(dp[cur]));
for(int S=0;S<=mx;S++){
update(S,S<<1);//不放
if(i>1&&(S>>(m-1)&1)==0) update(S,(S<<1)|(1<<m)|1);//上放
else if(j>1&&(S&1)==0) update(S,S<<1|3);//左放
}
}
}
printf("%lld\n",dp[cur][mx]);
}
return 0;
}