Mondriaan's Dream POJ - 2411 (建模型状压dp)

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.

思路:看了大佬博客,发现了一种方法。

分析:(来自大佬博客)

首先我们定义如下这种填充表示方式:如果一个骨牌是横着放的,那么它所在的两个方格都填充1.如果它是竖着放的,那么它所在的两个格子中,上面的那个填0,下面的这个填1.如下图所示:

由此可以得到断言:该矩阵的骨牌摆放方法和该矩阵的二进制表示法是一一对应的。 

   而且如果给定了前i行的二进制形式,那么前i-1行的骨牌摆放方式就可以100%确定了。

然后我们还可以论证出,对于任意连续的两行i-1和i行来说,第i-1行的二进制表示和i行的二进制表示有一个兼容的关系。

如上图例子,图中第1行为110011 通过分析我们知道它的下一行只能是001111或者111111或001100或111100(图中出现了001111和111111作为它的下一行)。为什么呢?

首先110011中的00表示这两个位置都是竖放的,则它的下一行必然是1.然后11这个位置(有两处,分别在头和尾)只可能是横放或者是该行之前的一行竖放留下来的尾巴,所以11这个位置对应的下一行只能是11或者00.

所以对于上图来说任意两个6位二进制数都有可能存在兼容关系(如110011兼容001111或者111111或001100或111100 ,即51兼容15或63或12或60)。

我们只需要求出所有对应的兼容关系,就可以由第一行的合法二进制形式出现的次数以及它的后续兼容二进制合法值,推出最终我们要求的整个矩阵的摆放方法数。

对于DP的状态设计,我们设d[i][num]=x表示第i行摆放骨牌的二进制形式对应的十进制值为num,且从第0行到第i行这之间的所有相邻行都兼容时,有多少种摆放方式。

比如d[3][1101]=3表示当前已经确定了前三行的合法摆放方式且第3行为1101时,有多少种摆放方法。

Y

Y

Y

Y

Y

Y

Y

Y

1

1

0

1

如下例所示:2*2的矩阵有多少种骨牌摆放方法呢?

1

1

1

1

0

0

1

1

只有以上两种。我们如何通过兼容关系推出这个结果呢?

首先第一行的合法二进制值是什么?肯定不是所有的二位二进制值都合法。我们假想在第一行上面还有一个第0行,而且它的二进制值是11(为什么这个假象合法?请思考),所以能和11兼容的二进制值(我们指的是能做11下一行的二进制值)就是合法的第一行二进制值。且这个11出现的次数我们定为1次,然后由1次11产生后续所有的兼容值,最后我们要求的是第二行的二进制值为11出现的次数。(为2,由由于0行11兼容 1行的11和00,且出现1次,则1行的11和00各出现1次,由于1行的11和00分别兼容2行的11且2行为尾行只要算11出现的次数即可,所以2*2矩阵总方法数为2)

所以我们现在要求各种对应的兼容关系,并且设第0行的11…111出现1次,则可以计算出最后一行的11…111出现了多少次,该值即为所求矩阵摆放骨牌的方法数。

现在我们专注于这个问题:如何求相邻两行二进制值的对应关系?可以枚举i-1行的所有二进制值情况,然后判断这个值本身是否合法,如果合法(其实只要这行是中间行所有二进制值都合法的,因为首行我们虚构了第0行为全1序列,最后一行我们只需要全1序列对应的值),再通过它推断出和它兼容的第i行二进制值(和i-1行二进制值兼容的第i行二进制为:第i-1行为0的位,第i行应为1。第i-1行为1的位,第i行为0或1,且如果第i行为1那么表示第i行此时的对应位置是横着放的,应该有偶数个连续的1才合法,只需判断i-1行中为1的位在第i行中如果也为1必须偶数个这样的1相连)。

我的理解:

首先将两种摆放方式(横放11或竖放(上为0,下为1)),然后假想第0行为111.....,这样假设是因为当0行为全1时,真的第一行开始放时,不受限制。

采用枚举的方式枚举相邻两行的可能状态(和其他状压dp类似,只不过这个比较难以枚举)

枚举的思路是:假如i行的k位是1,那么i+1的k位可以是1,0.

当k为0时,i+1的k只能是1.

最后从0行开始,递推到n行,最后只要n行满足全1,就是答案。(因为第n行的全1,可以兼容前面行的任意情况)

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <string>
#include <math.h>
#include <stack>
#include <bitset>
typedef long long ll;
#define INF 0x3f3f3f3f
const int maxn=5e6+10;
const int MAXN=1e3+10;
const long long mod=100000000;
using namespace std;
int n,m;
int top;
int state[maxn][2];
ll dp[15][1<<15];
inline void get()
{
    int up=(1<<m)-1;
    for(int i=0;i<=up;i++)
    {
        for(int j=0;j<=up;j++)
        {
            bool ok=1;
            for(int k=0;k<m;k++)
            {
                if(ok)
                {
                    if(((1<<k)&i)==0)
                    {
                        if(((1<<k)&j)==0)
                        {
                            ok=0;
                            break;
                        }
                    }
                    else
                    {
                        if(((1<<k)&j)==0) continue;
                        k++;
                        if(k>=m||((1<<k)&i)==0)
                        {
                            ok=0;
                            break;
                        }
                        else
                        {
                            if(((1<<(k-1))&j)&&((1<<k)&j)==0)
                            {
                                ok=0;
                                break;
                            }
                            else if(!((1<<(k-1))&j)&&(1<<k)&j)
                            {
                                k--;
                            }
                        }
                    }
                }
            }
            if(ok)
            {
                state[top][0]=i;
                state[top++][1]=j;
            }
        }
    }
}
int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    while(scanf("%d%d",&n,&m)!=EOF&&n+m)
    {
        top=0;
        if(n<m) swap(n,m);
        get();
        memset(dp,0,sizeof(dp));
        dp[0][(1<<m)-1]=1;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<top;j++)
            {
                dp[i+1][state[j][1]]+=dp[i][state[j][0]];
            }
        }
        printf("%lld\n",dp[n][(1<<m)-1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/81750428