Mondriaan's Dream
Time Limit: 3000MS Memory Limit: 65536K
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
Source
Ulm Local 2000
这可能是一道轮廓线dp的经典题。
我更愿意把他叫做贪吃蛇233,显然是因为他的走的路线有点像贪吃蛇啊233
具体是怎么样的呢?显然其他思路不是很科学,所以我们来学一种新的姿势。
对于每一个dp状态,你可以看成你有一条蛇在这个地图上爬。由于你每个格子只有两种状态,已覆盖和没有被覆盖(1或0),所以你可以状压。
地图:
1 2 3
4 5 6
7 8 9
你的路线:
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
你蛇的长度是什么呢?是你的地图的宽度。
这样有什么好处呢? 你可以保证每一列你都有身体的一部分在这个上面。
那么又为什么要这样呢?你可以思考一下就会发现,假设你蛇现在在5 6 7,你即将走8,由于题目你每个格子可以分成向上放,向左放,和不放。
向上放:你需要保证你上方的格子已经被覆盖了才可以(不然你上面的格子就没有机会被覆盖了)。
向左放:首先你需要保证你不是最左边的格子,然后你左边的格子没有被覆盖,上面的格子已经被覆盖了。
不放:你只需要保证你上面的格子被覆盖了。。。
综上,你会发现你的转移有且仅与你的上一次转移有关系,所以你蛇的长度正好可以保证保留一整套完整的状态。当然,也就可以滚动数组了。
感觉妙妙的样子~
感觉自己位运算的能力提高了(原来应该是没有233)
//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = (1 << 11);
int n, m, d, lim;
long long dp[2][maxn];
inline int clear(int t, int a)
{
return (t ^ (1 << (a - 1)));
}
inline bool check(int t, int a)
{
return (1 & (t >> (a - 1)));
}
inline int workk(int t)
{
int ret = clear(t, m);
ret <<= 1; ret |= (1 << 1); ret |= 1;
return ret;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
if(n == 0) return 0;
memset(dp, 0, sizeof(dp)); d = 0; dp[d][(1 << m) - 1] = 1; lim = (1 << m);
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
d ^= 1; memset(dp[d], 0, sizeof(dp[d]));
for(int k = 0; k < lim; ++k)
{
if(check(k, m))
{
dp[d][clear((k << 1), m + 1)] += dp[d ^ 1][k];
}
if(!check(k, m))
{
dp[d][((k << 1) + 1)] += dp[d ^ 1][k];
}
if(!check(k, 1) && check(k, m) && (j != 1))
{
dp[d][workk(k)] += dp[d ^ 1][k];
}
}
}
}
printf("%lld\n", dp[d][(1 << m) - 1]);
}
return 0;
}