1. 程式人生 > >哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)B 小樂樂搭積木 (狀態壓縮)

哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)B 小樂樂搭積木 (狀態壓縮)

時間限制:C/C++ 1秒,其他語言2秒

空間限制:C/C++ 32768K,其他語言65536K
64bit IO Format: %lld

題目描述

小樂樂想要給自己搭建一個積木城堡。 積木城堡我們假設為n*m的平面矩形。 小樂樂現在手裡有1*2,2*1兩種地磚。 小樂樂想知道自己有多少種組合方案。

輸入描述:

第一行輸入整數n,m。(1<=n,m<=10)

輸出描述:

輸出組合方案數。
示例1

輸入

複製
2 3

輸出

複製
3

說明

示例2

輸入


1 3

輸出

複製
0
示例3

輸入


2 5

輸出


8

 

題目大意:

給你一塊n*m的方塊,以及若干1*2的小木板,問你有多少種方式能夠使小木板填滿方塊。

 

狀壓DP模板題。和POJ2411雷同。

摘自focus_best的csdn部落格:

首先我們定義如下這種填充表示方式:如果一個骨牌是橫著放的,那麼它所在的兩個方格都填充1.如果它是豎著放的,那麼它所在的兩個格子中,上面的那個填0,下面的這個填1.由此可以得到斷言:該矩陣的骨牌擺放方法和該矩陣的二進位制表示法是一一對應的。 

現在我們專注於這個問題:如何求相鄰兩行二進位制值的對應關係?可以列舉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相連)。

 

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
typedef long long ll;
const int mod=1000000007;
const int inf=1000000000;
const int maxn=10;
const int maxm=200;

int n,m;
int dp[maxn+5][2<<(maxn+5)];

int main()
{
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    dp[0][(2<<(m-1))-1]=1;

    for(int i=1; i<=n; i++)
    {
        for(int k=0; k<=((2<<(m-1))-1); k++)//i行
        {
            if(i==n&&k<((2<<(m-1))-1))
                continue;
            for(int j=0; j<=((2<<(m-1))-1); j++)//i-1行
            {
                bool flag=true;
                for(int p=0,cnt=0; p<=m-1; p++)
                {
                    if((j&(1<<p))==0)
                    {
                        if(cnt%2)
                            flag=false;
                        else
                            cnt=0;
                        if((k&(1<<p))==0)
                            flag=false;
                    }
                    if((j&(1<<p))>0)
                    {
                        if((k&(1<<p))>0)
                            cnt++;
                        else
                        {
                            if(cnt%2)
                                flag=false;
                            else
                                cnt=0;
                        }
                    }
                    if(p==m-1)
                    {
                        if(cnt%2)
                            flag=false;
                    }
                    if(!flag)
                        break;
                }
                if(flag)
                {
                    dp[i][k]+=dp[i-1][j];
                }
            }
        }
    }

    printf("%d\n",dp[n][((2<<(m-1))-1)]);

    return 0;
}
View Code