poj2411 Mondriaan's Dream 状压DP

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 22073   Accepted: 12368

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

题意:用 1 * 2 的瓷砖覆盖 n * m 的地板,问共有多少种覆盖方式?

思路:

定义状态:

我们首先定义状态:dp[j][state],表示能到达第j列,且第j列所有方格的覆盖情况为state时的所有方法数;

这样我们通过这个状态把这个问题分为j个阶段(j代表列数),因为第j列放置的情况最多只能影响到j+1列。比如第j列放置

一个或多个1*2的方格。这样一种情况就会影响到j+1列;但是他最多影响到第j+1列,之后就不能再影响了。

这就是无后效性;无后效性是保证能动态规划的关键。

状态转移:

当第i列第j行状态为0时,表示没有覆盖;当为1时,说明被覆盖了;

如果当前格没有被覆盖,说明我们至少可以放一个1*2的方格,如果当前列的下一行也没有被覆盖,那么我们可以放一个

2*1的方格;如果被覆盖, 那么我们继续往当前列的下一行遍历 ,直到把这一列遍历完; 在这个过程中 ,我们能求出

下一列的合法状态(也就是可以到达的状态),这是一个搜索的过程,至于为什么要搜索,请看下面:

我们把当前列每一种状态都进行搜索,看能不能找到从这种状态到其他状态的一种路径;如果存在,说明其他状态是

可以到达的。所以我们其实并不需要对每一种状态进行搜索,只需要对可以到达的状态进行搜索即可;

按一个方向求出该问题的解:

当前阶段总是影响下一阶段,所以我们应该按照阶段的方向进行求解,在这个题中是按列的方向求解;题目要求全部填

满,所以答案应该是dp[m+1][0];

#include<iostream>
#include<string.h>
#define ll long long
using namespace std;
ll dp[15][2100];//dp[j][state],表示能到达第j列,且第j列所有方格的覆盖情况为state时的所有方法数
int n,m;
void dfs(int i,int j,int state,int next)
{
    if(i==n)
    {
        dp[j+1][next]=dp[j+1][next]+dp[j][state];//因为后一列是在前一列的基础上操作的,所以是加和
        return;
    }
    else
    {
        if((state&(1<<i))==0)//判断数字state的第i位数字是否为0,0表示该格子没被覆盖,说明可以放一个1*2的木板 
            dfs(i+1,j,state,next|(1<<i));//next|(1<<i)是将next的第i+1位改为1
        if(i+1<n&&(state&(1<<i))==0&& (state & (1 << (i+1))) == 0)
        //当该格子没被覆盖且它下面的格子也没被覆盖 ,说明可以放一个2*1的木板
            dfs(i+2,j,state,next);
        if((state&(1<<i))>0)//当前格子被覆盖
            dfs(i+1,j,state,next);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)&&(n+m))
    {
        if(n>m)//降低时间复杂度
        {
            int temp;
            temp=n;
            n=m;
            m=temp;
        }
        memset(dp,0,sizeof(dp));
        dp[1][0]=1;//如果m=1(一列),只有一种摆放方案
        for(int j=1;j<=m;j++)//从第一列开始,到第m列(取等),求每一列的摆放方案
        {
            for(int state=0;state<(1<<n);state++)//state是每一列的摆放状态,用一个十进制数的每一位表示
            {
                if(dp[j][state]>0)
                    dfs(0,j,state,0);
            }
        }
        printf("%lld\n", dp[m + 1][0]);
    }
    return 0;
}


猜你喜欢

转载自www.cnblogs.com/-citywall123/p/10864119.html