POJ 3411 Mondriaan's Dream 【状压Dp】 By cellur925

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

(一句话题意)

本题的数据范围很小,nm均为11,这提示我们可以用状压。我们可以预处理出所有横放骨牌的状态,即表示为一个二进制串,如果为1,则证明这里有骨牌,为0,则证明这里没有骨牌。

 1 bool check(int x)
 2 {
 3     bool ok=1;
 4     for(int i=0;i<n;i++)
 5     {
 6         if(x&(1<<i)) ok=ok^1;
 7         //当前位为1,记下为1的个数,最终必须两两相邻且个数为偶数
 8         if((!(x&(1<<i)))&&!ok) return 0;
 9     }
10     if(ok) return 1;
11     else return 0;
12 }

我们考虑设计一个状态,f[i][j]表示当前在第i行,当前行状态为j的方案数,我们的转移目标是f[m][(1<<n)-1](即在最后一行,每个地方都被填满)

则可以写出转移方程:f[i][j]=+f[i-1][bin[k]^j^fAKe](k枚举上一行的状态,满足条件k是j的子集,也就是能转移到当前状态的上一状态)两次异或是在求能转移到当前状态的上一状态。按学长的话说就是:那么我们对于每个状态,如果我们知道了它哪些是横放的,那么我们也就可以推出它上一行的状态,因为当前行必须要把上一行补满。

注意细节:开long long(WA了好几次)

code

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4
 5 using namespace std;
 6 typedef long long ll;
 7
 8 int n,m,cnt;
 9 int bin[3000];
10 ll f[12][2050];
11
12 bool check(int x)
13 {
14     bool ok=1;
15     for(int i=0;i<n;i++)
16     {
17         if(x&(1<<i)) ok=ok^1;
18         if((!(x&(1<<i)))&&!ok) return 0;
19     }
20     if(ok) return 1;
21     else return 0;
22 }
23
24 void init()
25 {
26     memset(f,0,sizeof(f));
27     memset(bin,0,sizeof(bin));
28     cnt=0;
29 }
30
31 int main()
32 {
33
34     while(scanf("%d%d",&n,&m))
35     {
36         if(n==0) break;
37         int fAKe=(1<<n)-1;
38         for(int i=0;i<=fAKe;i++)
39              if(check(i)) bin[++cnt]=i;
40          for(int i=1;i<=cnt;i++) f[1][bin[i]]=1;
41          for(int i=2;i<=m;i++)
42           for(int j=0;j<=fAKe;j++)
43            for(int k=1;k<=cnt;k++)
44             if((bin[k]|j)==j) f[i][j]+=f[i-1][bin[k]^j^fAKe];
45          printf("%lld\n",f[m][fAKe]);
46         init();
47     }
48     return 0;
49 }

注:我们这里说的子集概念,举个例子:

{1,3,4}为一集合,则二进制第1位或第三位或第4位上为1的所有数称为它的子集。由于或运算满足“有一个1即为1”,则我们通过或运算可以来检查是否存在子集关系。

POJ 3411 Mondriaan's Dream 【状压Dp】 By cellur925

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9445523.html

时间: 2024-10-09 21:40:24

POJ 3411 Mondriaan's Dream 【状压Dp】 By cellur925的相关文章

POJ 2411 Mondriaan&#39;s Dream(状压DP)

http://poj.org/problem?id=2411 求一个n*m矩阵用1*2方块去填满的情况有几种 思路:状压dp,先预处理那些状态之间能互相到达,情况就几种,上一个两个1,下一个状态也两个1,上一个为0,下一个必须为1,还有一种是上一个为1,下一个为0的情况 然后就一层层往后递推即可 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; int

POJ 2411 Mondriaan&#39;s Dream ——状压DP 插头DP

[题目分析] 用1*2的牌铺满n*m的格子. 刚开始用到动规想写一个n*m*2^m,写了半天才知道会有重复的情况. So Sad. 然后想到数据范围这么小,爆搜好了.于是把每一种状态对应的转移都搜了出来. 加了点优(gou)化(pi),然后poj上1244ms垫底. 大概的方法就是考虑每一层横着放的情况,剩下的必须竖起来的情况到下一层取反即可. 然后看了 <插头DP-从入门到跳楼> 这篇博客,怒抄插头DP 然后16ms了,自己慢慢YY了一下,写出了风(gou)流(pi)倜(bu)傥(tong)

poj 2411 Mondriaan&#39;s Dream 状压dp入门

题意: 求h*w的矩形被1*2的小矩形覆盖的方案数. 分析: 状压dp入门,<挑战程序设计竞赛>上讲的很好,好几天才看懂. 代码: #include <iostream> using namespace std; __int64 ans[16][16]; int n,m; __int64 dp[2][1<<16]; __int64 solve() { int i,j,used; memset(dp,0,sizeof(dp)); __int64 *crt=dp[0],*n

POJ 2411 Mondriaan&#39;s Dream (状压DP)

题意:给出一个n*m的棋盘,及一个小的矩形1*2,问用这个小的矩形将这个大的棋盘覆盖有多少种方法. 析:对第(i,j)位置,要么不放,要么竖着放,要么横着放,如果竖着放,我们记第 (i,j)位置为0,(i+1,j)为1,如果横着放,那么我们记 (i,j),(i,j+1)都为1,然后dp[i][s]表示 第 i 行状态为 s 时,有多少方法,那么我们就可以考虑与和匹配的状态,这个也很容易判断. 代码如下: #pragma comment(linker, "/STACK:1024000000,102

POJ 2411 &amp;&amp; HDU 1400 Mondriaan&#39;s Dream (状压dp 经典题)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12341   Accepted: 7204 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 2411【题解】Mondriaan&#39;s Dream 状压DP

题目链接:http://poj.org/problem?id=2411 把每一行当作一个二进制状态. 1表示是一个竖着的1*2的方格. 0表示其他状态. 那么显然当i-1的状态k能转移到i的j: 1.j 和 k 的按位与为0.(有1必须要0,0也可以有1) 2.j 和 k 按位或每一段0都有偶数个.(表示横着的长方形) 那么就可以预处理一下合格的点. 然后状压DP. 代码如下: #include<cstdio> using namespace std; int n,m; long long f

POJ 2411 Mondriaan&#39;s Dream (状压+dfs)

Language: Default Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12405   Accepted: 7239 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in

poj2411--Mondriaan&#39;s Dream(状压dp+dfs)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12315   Accepted: 7189 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 1185 炮兵阵地(状压DP入门)

http://poj.org/problem?id=1185 状压DP: 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int dp[105][100][100]; 7 int ma[105],st[105]; 8 9 int ok(int x) 10 { 11 return (x&(x<<1))+(x&(x&