poj 2411 Mondriaan's Dream

題意:給你一個1*2的矩陣進行填充,結果求填滿的種類數。

分析:下面是我引用過來的說明,很不錯。

最上面的爲第0行,最下面爲第n-1行
從上到下按行DP
其中一行的狀態我們用一個二進制表示,0表示沒有被覆蓋,1表示被覆蓋了
最後得到一個01串,這個串變回十進制就是一個狀態
定義狀態dp[i][s],表示前i-1行已經放滿,第i行的狀態爲s的方案數
狀態轉移方程爲 dp[i][s]=sum{ dp[i-1][ss] } ,其中狀態s與狀態ss兼容
這個狀態轉移方程的內涵在於理解s和ss何爲兼容
首先我們約定一個放置方法,就是豎着放的時候,我們暫且將其稱爲“上凸型擺放”
因爲豎放必然佔據第i-1行和第i行,我們約定這個方塊是屬於第i行的,也就是說它凸上去了
那麼要在第i行的第j列豎放一個方塊的話,第i-1行第j列必須沒有方塊
也就是說,第i行的放置是受到第i-1行的限制的,反過來說在第i行豎放了方塊,也會影響第i-1行的狀態
所以這樣就可以講解一下狀態轉移方程了,前i-2行已經放滿了,第i-1行的狀態爲ss(dp[i-1][ss])
此時在第i行開始放一些方塊,放的方法不定,可能橫放可能豎放,但是按這個方案放完後
第i-1行剛好被填滿,且第i行的狀態變爲了s,所以不難想到第i-1行的狀態ss到第i行的狀態s這個轉移是唯一的
所以有 dp[i][s]=sum{ dp[i-1][ss] }
最後我們詳細討論一下s和ss在什麼情況下是兼容的
1.第i行的第j列爲1,第i-1行的第j列爲1,這樣的話,說明第i行的第j列一定不是豎放而是橫放否則會與第i-1行的第j列衝突
  所以馬上緊接着判斷第i行第j+1列,如果是1,那麼滿足橫放的規則,同時也要第i-1行第j+1列也要爲1,否則的話這個格子沒辦法填充,
  成立後向左移動兩格
  不滿足上述條件的,就是兩個不兼容或者不合法的狀態
2.第i行第j列爲1,第i-1行第j列爲0,那麼說明第i行第j列應該豎放並填充第i-1行第j列,成立後向左移動一格
3.第i行第j列爲0,說明不放方塊,那麼第i-1行第j列必須爲1,否則沒法填充這個格子。若第i-1行第j列也爲0,不兼容不合法
  (至於第i行第j列這個格子空着幹什麼,其實就是留出來給第i+1行豎放的時候插進來的)

那麼目標狀態是什麼,就是dp[n][maxs],maxs表示全部是1的串,即第n-1行以上全部覆蓋滿,第n行的狀態爲maxs,即沒有空着的格子,也全部覆蓋滿了
即整個矩形全部被覆蓋滿了的狀態

最後是第1行的初始化問題,因爲約定了“上凸型擺放”,所以第1行是不能豎放方格的,只能橫放方格,
每橫放一個必定佔據兩個格子,所以在判斷一個狀態(那個01串)的時候,連着的1的個數必定爲偶數,如果出現了單獨的1,說明不合法


下面附代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1<<12;
ll dp[13][maxn];
int n,m;
bool judge(int val){
    int flag=0;
    while(val){
        if(val%2){
            flag=(flag+1)%2;
        }
        else{
            if(flag==1){
                return false;
            }
        }
        val>>=1;
    }
    if(flag) return false;
    return true;
}
bool ok(int tmp_1,int tmp_2){
    for(int i=0;i<m;){
        if(tmp_1 & (1<<i)){
            if(tmp_2 & (1<<i)){
                if(i==m-1) return false;
                if(!(tmp_2 &(1<<(i+1)))) return false;
                if(!(tmp_1 &(1<<(i+1)))) return false;
                i+=2;
            }
            else{
                i++;
            }
        }
        else{
            if(!(tmp_2 & (1<<i))){
                return false;
            }
            else{
                i++;
            }
        }
    }
    return true;
}
int main(){
    while(~scanf("%d %d",&n,&m) && (n+m)){
        if( (n*m)%2 ){
            printf("0\n");
            continue;
        }
        if(n<m){
            int t=n;
            n=m;
            m=t;
        }
         memset(dp,0,sizeof(dp));
         int Max=(1<<m);
         for(int i=0;i<Max;i++){
            if(judge(i))
               dp[0][i]=1;
         }
         for(int i=1;i<n;i++){
            for(int j=0;j<Max;j++){
                for(int k=0;k<Max;k++){
                    if(ok(j,k)){
                        dp[i][j]+=dp[i-1][k];
                    }
                }
            }

         }
         printf("%I64d\n",dp[n-1][Max-1]);
    }
}


發佈了29 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章