状态压缩DP POJ 2411 Mondriaan's Dream

来源:互联网 发布:系统检测安全数据 编辑:程序博客网 时间:2024/05/20 01:36

题目:

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 21 31 42 22 32 42 114 110 0

Sample Output

10123514451205

这个题目,讲道理思路是很多的。

但是要想找到合适的方式来表达状态,确实不容易。

如果不是今天在这之前做了另外一个题目:棋盘问题

我可能还真不会想到这个方法。

代码:

#include<iostream>#include<string.h>using namespace std;int h, w;long long dp[11][2048];//前i行的和long long sum;//对于每一行,横为0,竖为1,所以00011是不可能的,00100是可能的bool ok(int n)//每2个0都是在一起的{n += (1 << w);//因为不知道维度的奇偶性while (n){if (n % 2)n /= 2;else{if (n % 4)return false;n /= 4;}}return true;}int main(){while (cin >> h >> w){if (h == 0)break;if (h == 1){cout << (w + 1) % 2 << endl;continue;}if (h % 2 && w % 2){cout << 0 << endl;continue;}memset(dp, 0, sizeof(dp));for (int i = 0; i < (1 << w); i++)if(ok(i))dp[0][i] = 1;for (int i = 1; i < h-1; i++){for (int j = 0; j < (1 << w); j++){for (int k = 0; k < (1 << w); k++){if (j&k)continue;if (ok(j^k)){dp[i][j] += dp[i - 1][k];}}}}sum = 0;for (int k = 0; k < (1 << w); k++)if (ok(k))sum += dp[h - 2][k];cout << sum << endl;}return 0;}

首先,怎么样把一行状态压缩。

如果骨牌是横着的,那么就是00,如果骨牌是竖着的,那么就是1(上面1行或者下面一行也有个1)

然后直接按照顺序串联起来。

然后,dp二维数组是什么呢?

就是前i行的和为j的情况数。

我觉得我并没有说清楚前i行的和是什么鬼,不过不重要,

这么说吧,如果第i行的某个格子是00中的0,或者11中下面的1,那么它对应 j 里面的0,

如果它是11中上面的1,即它和它下面一行的那个格子组成骨牌,那么它对应 j 里面的1。

也就是相当于,画一条横线标明前i行,j 就是那些被割开的骨牌。


1 0
原创粉丝点击