Mondriaan'sDream
Time Limit: 3000MS | Memory Limit: 65536K |
Total Submissions: 10821 | Accepted: 6294 |
Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan.One night, after producing the drawings in his 'toilet series' (where he had touse his toilet paper to draw on, for all of his paper was filled with squaresand rectangles), he dreamt of filling a large rectangle with small rectanglesof width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need acomputer to calculate the number of ways to fill the large rectangle whosedimensions were integer values, as well. Help him, so that his dream won't turninto a nightmare!
Input
The input contains several test cases. Each test case is made upof 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 givenrectangle can be filled with small rectangles of size 2 times 1. Assume thegiven large rectangle is oriented, i.e. count symmetrical tilings multipletimes.
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
【题目大意】就是有一个大的矩形(有h,w决定),和一个固定大小的2*1的小矩形,让小矩形来填满这个大的矩形,问总共有多少种填法;
【分析】:这道题的难点在于如何找出比较合适的状态表示;横放和竖放分别要怎么表示呢?以及他们的约束条件,这么放才能有保证能填满;显然需要知道上一层的状态所以需要定义二维数组dp[state][i];
不妨我们将横放定义为1,1;将竖放定义为0,1因此,我们有假如知道了上一层的状态为1,显然该位置与上一层无关(该位置可以为0或者1),如果上一层为0,那么该位置则必须为1;
约束条件:若行为奇数,那么该行0的个数必须为奇数,如果该行为偶数那么该行的0的个数也应该为偶数;
【状态方程】dp[state][i]:表示当前为state且行数为i的种类的总数;
【状态转移方程】dp[state][i] =遍历 dp[state’][i-1] ;
【边界条件】dp[][0]; 第0层的state
难点:在于判断这一层的state与上一层state’是否能够兼容,我是看了网上大神的代码也知道用或和与来判断;
Java代码如下:
import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;
public class Main {
private long dp[][];
private int h, w;
public Main(int h, int w) {
this.h = h;
this.w = w;
dp = new long[1 << w][h];
for (int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], 0);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner cin = new Scanner(System.in);
int h, w;
h = cin.nextInt();
w = cin.nextInt();
Main ma;
while (!(h == 0 & w == 0)) {
ma = new Main(h, w);
System.out.println(ma.getSum());
h = cin.nextInt();
w = cin.nextInt();
}
}
private long getSum() {
// TODO Auto-generated method stub
// 应该获取可以有的情况,就是说0的个数必须与那个相同;
// 若只有一行
if (h == 1) {
// 那么w必须为偶数
if (w % 2 == 0) {
return 1;
} else {
return 0;
}
}
boolean isRight = true;
int temp;
int count;
boolean flag;
// 获得第零行,用穷举的方法;
for (int i = 0; i < dp.length; i++) {
// 在0出现时,必须要有1必须是成对出现的;
// 也就是说第一个0必须出现在2,4,6,8,。。。的位置;
// 第二个零必须出现在奇数的位置上;依次反反复复直到结束
// 与此同时,0的个数必须与列数w的奇偶性相同
isRight = true;
temp = i;
count = 0;
flag = true;
for (int j = 0; j < w; j++) {
if ((temp & 1) == 0) {
count++;
if (isRight) {
temp = temp >> 1;
continue;
} else {
flag = false;
break;
}
} else {
isRight = !isRight;
}
temp = temp >> 1;
}
if (flag) {
// 判断奇偶性要相同
if (count % 2 == w % 2) {
dp[i][0] = 1;
}
}
}
// 一行一行来,从第一行开始,
for (int i = 1; i < h; i++) {
// 遍历上一层的状态
for (int j = 0; j < dp.length; j++) {
if (dp[j][i - 1] == 0) {
continue;
}
if (i == h - 1) {
// 最后一层,只能全一
if (isOk(j, dp.length - 1)) {
dp[dp.length - 1][i] = dp[dp.length - 1][i]
+ dp[j][i - 1];
}
} else {
for (int k = 0; k < dp.length; k++) {
if (isOk(j, k)) {
// dp[state[k]][i] =
// dp[state[k]][i]+dp[state[j]][i-1];
dp[k][i] = dp[k][i] + dp[j][i - 1];
}
}
}
}
}
return dp[dp.length - 1][h - 1];
}
private boolean isOk(int preState, int nextState) {
// TODO Auto-generated method stub
// 首先,如果上层为零,那么下层必须为1;,相与就能把所有的情况取出
if ((preState | nextState) != dp.length - 1) {
return false;
}
// preState&nextState这个结果会是什么呢?相与能够达到将每个只要是竖放的都能被置为0
// 那么判断方法就和1一模一样了;因此,不妨利用dp[][0];所以有:
if (dp[preState & nextState][0] == 1) {
return true;
}
return false;
}
private boolean judgy(int s) {
// TODO Auto-generated method stub
// 获取s中零的个数,同时我们知道我们需要知道的s的个数为w
int count = 0;
for (int i = 0; i < w; i++) {
if ((s & 1) == 0) {
count++;
}
s = (s >> 1);
}
if (count % 2 == w % 2) {
return true;
}
return false;
}
}