Mondriaan’s Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 17936 | Accepted: 10285 |
Description
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
Output
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
Source
[Submit]
[Go Back] [Status]
[Discuss]
题意
给你一个大小为 h∗w 的方格,让你用 1∗2 的多米诺骨牌将该棋盘覆盖满,求方案数。
思路
这题由于h,w的范围非常小,所以我们可以很轻易地想到状压DP。
P.S. 本题题解参考http://blog.csdn.net/xingyeyongheng/article/details/21692655
对于第k行第j列,有3种情况将该点铺满由第k-1行第j列砖竖着铺将第k行第j列铺满
由第k行第j列被横铺砖铺满
第k行第j列砖竖着铺将该点铺满所以对于每一列的情况其实有两种(1,0)表示该点铺砖还是不铺
而对于每一列必须到达的状态只有一种,就是被铺满(1)
但是由上述3种情况将铺满方式分成两种:
0和1表示被k-1行j列竖铺铺满和在k-1行被横铺铺满
对于每一行列举每一种到达的状态j,dp[j]表示到达该状态有多少种情况
分析对于第k-1行状态j:10000111
需要到达第k行状态i: 01111011
如果需要到达第k行j列状态是0,则必须第k-1行该点状态不能是0,否则一定是连续两列竖放冲突
所以到达第k-1行该点只能是1,也就是说i|j一定每一位是1,也可以一步步判断是否满足第k行j列是0第k-1行j列是1
如果需要到达第k行状态j列是1,则假如第k-1行该点是0,则该点状态可以到达,继续判断j+1列
假如第k-1行该点是1,则第k行j列的1一定是横铺到达的,所以k行第j+1列一定也被铺满为1
从而第k-1行j+1列一定不能竖铺,必须被横铺铺满,所以也是1.
于是综合的第k行j列和第k-1行j列的关系(每一行每一列都表示到达的状态)1:下面这种情况从第j列继续去判断j+1列
1
0
2:下面这种情况从第j列继续去判断j+1列
0
1
3:下面这种情况从第j列判断第j+1列是否全是1,然后继续判断第j+2列
1
1
Code
#pragma GCC optimize(3)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<climits>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<stack>
#include<climits>
#include<vector>
using namespace std;
typedef long long ll;
inline void readInt(int &x) {
x=0;int f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
inline void readLong(ll &x) {
x=0;int f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
/*================Header Template==============*/
const int maxn=(1<<11);
ll f[2][maxn];
int maxstatus[12],n,m;
bool mark[maxn];
inline void init(){maxstatus[0]=1;for(int i=1;i<=11;i++)maxstatus[i]=maxstatus[i-1]*2;}
inline bool check(int statu) {
while(statu) {
if(statu&1) {
statu>>=1;
if(!(statu&1))
return 0;
statu>>=1;
}
else
statu>>=1;
}
return 1;
}
int main() {
init();
while(scanf("%d%d",&n,&m)==2&&n&&m) {
memset(f,0,sizeof f);
memset(mark,0,sizeof mark);
if((!n)||(!m)||((n*m)&1)) {
puts("0");
continue;
}
if(n<m)
swap(n,m);
for(int sta=0;sta<maxstatus[m];sta++)
if(check(sta)) {
f[0][sta]=1;
mark[sta]=1;
}
int now=0;
for(int i=2;i<=n;i++) {
now^=1;
for(int sta=0;sta<maxstatus[m];sta++)
f[now][sta]=0;
for(int sta=0;sta<maxstatus[m];sta++) {
for(int pre=0;pre<maxstatus[m];pre++) {
if((sta|pre)!=maxstatus[m]-1||(!mark[sta&pre]))
continue;
f[now][sta]+=f[now^1][pre];
}
}
}
printf("%lld\n",f[now][maxstatus[m]-1]);
}
return 0;
}