题目链接:http://poj.org/problem?id=2411
Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 12131 | Accepted: 7075 |
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!
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题目大意:给你一个h*w的长方形,叫你用1*2的小长方形填满它,问共有几种填法。
算法:状压DP,用横放用1 1表示,竖放用 0 表示
1
例如一个2*2的长方形可以这样填充
1 | 1 |
1 | 1 |
0 | 0 |
1 | 1 |
即两个小长方形都横放和都竖放。
每一行用一个数的二进制表示一个状态
可以想象,第一行的合法状态满足这样一个性质,如果出现1,则连续1的个数必定为偶数个,否则该状态不合法。例如0000是合法的,0001就不合法。0011合法。
然后判断上下两行的状态是否相容即这两行是否被小长方形填满,设它们分别为x,y如果(x|y)!=(1<<m)-1的话,说明它们的某一位都为0,即出现竖着的00,这显然是不合法的。
满足了上一个条件之后,再者就是还有判断它们横放是否合法,就是判断(x&y)是否是合法状态,这个简单,因为我们已经预处理过了。
接下来就是状态转移,当前行的状态j,等于它上一行所有与它相容状态的和,即dp[j][i]=sum(dp[k][i-1]);
下面是AC代码:
#include<cstdio>
#include<cstring>
#define LL long long
const int maxn=1<<11;
int Begin[maxn],n,m;
LL dp[maxn][12];
bool is(int x)
{
int cnt=0;
while(x)
{
if(x&1) cnt++;
else if(cnt&1) return 0;//此状态有连续奇数个1则状态不合法
x>>=1;
}
return !(cnt&1);//此状态有连续奇数个1则状态不合法,否则合法
}
void init()
{
memset(Begin,0,sizeof(Begin));
for(int i=0;i<(1<<11);i++)//找出所有合法状态
if(is(i)) Begin[i]=1;
}
bool judge(int x,int y)
{
if((x|y)!=(1<<m)-1) return 0;//如果个状态有某位同时出现0即竖着出现0,0,则这两个状态不兼容
return Begin[x&y];//看横放的木棍是否合法
}
int main()
{
init();
while(~scanf("%d%d",&n,&m)&&(n+m))
{
memset(dp,0,sizeof(dp));
for(int i=0;i<(1<<m);i++) dp[i][0]=Begin[i];//初始化第一行
for(int i=1;i<n;i++)//从第二行开始递推
for(int j=0;j<(1<<m);j++)//枚举本行所有状态
for(int k=0;k<(1<<m);k++)//枚举上一行所有状态
if(judge(k,j)) dp[j][i]+=dp[k][i-1];//合法则加起来
printf("%lld\n",dp[(1<<m)-1][n-1]);
}
return 0;
}
1 | 1 |
1 | 1 |
1 | 1 |
1 | 1 |