Mondriaan's Dream POJ - 2411 状压DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Waves___/article/details/74165341

Mondriaan's Dream
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 16749   Accepted: 9667

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

/*
题目链接:https://vjudge.net/problem/POJ-2411 
参考:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html
 
题意:给n与m的矩形,用1*2的矩形填充满,问有多少种方案 

最上面的为第1行,最下面为第n行
从上到下按行DP
其中一行的状态我们用一个二进制表示,0表示没有被覆盖,1表示被覆盖了
最后得到一个01串,这个串变回十进制就是一个状态
定义状态dp[i][s],表示前i-1行已经放满,第i行的状态为s的方案数
状态转移方程为 dp[i][s]=sum{ dp[i-1][ss] } ,其中状态s与状态ss兼容

这个状态转移方程的内涵在于理解s和ss何为兼容
首先我们约定一个放置方法,就是竖着放的时候,我们暂且将其称为“上凸型摆放”
因为竖放必然占据第i-1行和第i行,我们约定这个方块是属于第i行的,也就是说它凸上去了
那么要在第i行的第j列竖放一个方块的话,第i-1行第j列必须没有方块
也就是说,第i行的放置是受到第i-1行的限制的,反过来说在第i行竖放了方块,也会影响第i-1行的状态
所以这样就可以讲解一下状态转移方程了,前i-2行已经放满了,第i-1行的状态为ss(dp[i-1][ss])
此时在第i行开始放一些方块,放的方法不定,可能横放可能竖放,但是按这个方案放完后
第i-1行刚好被填满,且第i行的状态变为了s,所以不难想到第i-1行的状态ss到第i行的状态s这个转移是唯一的
所以有 dp[i][s]=sum{ dp[i-1][ss] }
最后我们详细讨论一下s和ss在什么情况下是兼容的
1.第i行的第j列为1,第i-1行的第j列为1,这样的话,说明第i行的第j列一定不是竖放而是横放否则会与第i-1行的第j列冲突
  所以马上紧接着判断第i行第j+1列,如果是1,那么满足横放的规则,同时也要第i-1行第j+1列也要为1, 
  若成立后向左移动两格,
  否则不兼容
2.第i行第j列为1,第i-1行第j列为0,那么说明第i行第j列应该竖放并填充第i-1行第j列,成立后向左移动一格
3.第i行第j列为0,说明不放方块,那么第i-1行第j列必须为1,否则没法填充这个格子。 
  (至于第i行第j列这个格子空着干什么,其实就是留出来给第i+1行竖放的时候插进来的)
4.第i行第j列为0,第i-1行第j列也为0,不兼容 .

那么目标状态是什么,就是dp[n][maxs],maxs表示全部是1的串,即第n-1行以上全部覆盖满,
第n行的状态为maxs,即没有空着的格子,也全部覆盖满了,即整个矩形全部被覆盖满了的状态

  最后是第1行的初始化问题,因为约定了“上凸型摆放”,所以第1行是不能竖放方格的,只能横放方格,
每横放一个必定占据两个格子,所以在判断一个状态(那个01串)的时候,连着的1的个数必定为偶数,
如果出现了单独的1,说明不合法。 
*/ 

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
#define LL long long
#define inf 0x3f3f3f3f
#define INF 1e18 
using namespace std;
typedef pair<int, int> P;
const int maxn = 2e5 + 5;
const int mod = 1e8;

LL dp[1<<12][12];
LL res[1<<12][12];
int n, m;
int main(void)
{
//	std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//	freopen("in.txt", "r", stdin); 
	memset(res, -1, sizeof res);
	while (scanf("%d %d", &n, &m) && (m || n)){ 
		if (n < m) swap(n, m); // 使m为较小数,减少状态 
		if ((n * m) % 2) //其中n*m为奇数的话,矩形面积就是奇数,不可能完全覆盖
			res[n][m] = 0;
		if (res[n][m] != -1){ // 记忆化 
			printf("%lld\n", res[n][m]);
			continue;
		} 
		memset(dp, 0, sizeof dp);
		for (int S = 0; S < (1 << m); S++){
			int flag = 1;
			for (int d = 0; d < m; d++){
				if (S & (1 << d)){
					if (d + 1 < m && (S & (1 << (d + 1)))) d++;
					else { flag = 0; break; }  // 不合法 
				} 
			}
			if (flag) dp[S][1] = 1;
		}
		for (int i = 2; i <= n; i++){ 
			for (int S1 = 0; S1 < (1 << m); S1++){ 
				for (int S2 = 0; S2 < (1 << m); S2++){
					int d = 0, flag = 1;
					while (d < m){
						if ((S1 & (1<<d)) && (S2 & (1<<d))){ // 都为1 
							if (d + 1 < m && S1 & (1<<(d+1)) && S2 & (1<<(d+1))) d += 2;
							else {  flag = 0; break; } // 不兼容 
						}	
						else if ((S1 & (1<<d)) && !(S2 & (1<<d))) d++; // 当前为1 上行为0 
						else if (!(S1 & (1<<d)) && (S2 & (1<<d))) d++; // 当前为0 上行为1 
						else if (!(S1 & (1<<d)) && !(S2 & (1<<d))) // 当前为0 上行为0,不兼容 
							{  flag = 0; break; }
					}
					if (flag) dp[S1][i] += dp[S2][i - 1];
				}
			}
		}
		res[n][m] = dp[(1<<m)-1][n];
		printf("%lld\n", dp[(1<<m)-1][n]);
	}

	return 0;
}

/*
input
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

output
1
0
1
2
3
5
144
51205 

*/













猜你喜欢

转载自blog.csdn.net/Waves___/article/details/74165341