走道鋪磚問題

n*m的走道鋪滿1*2的地磚,求鋪設方案數。
1 <= N,M <= 11

狀壓dp。我們知道這題中上一行的狀態可以一定程度上決定下一行,且鋪一塊磚的方式只有兩種:豎放和橫放。
不妨用1 1表示橫放的磚塊,上0下1來表示豎放的磚塊。爲什麼這樣表示?

  1. 橫放磚塊對下一行完全沒有影響
  2. 豎放磚塊的下半部分填充了下一行的一個格子。
  3. 豎放磚塊的上半部分對下一行有影響:如果上一行某一位是0,那麼下一行這位只能是1。
  4. 爲了保證最後一行沒有豎放的磚塊,我們只需要保證最後一行都是1。

用dp[i][j]表示第i行狀態爲j的方案數,那麼dp[n][2^m-1]就是答案。
之後就是bottom-up過程了,值得注意的是有許多非法情況需要判斷。

  1. 例如第i行第k位已經是0,那麼i-1行對應位一定是1,否則非法。如果合法繼續檢測(i,k+1)。
  2. (i,k)=1,那麼繼續分類:
    1. (i-1,k)=0,合法,繼續檢測(i,k+1)。
    2. (i-1,k)=1,則只可能是(i,k+1)=(i-1,k+1)=1,否則非法。如果合法繼續檢測(i,k+2)。
  3. 對於第一行:
    1. (0,k)=0,繼續檢測(0,k+1)。
    2. (0,k)=1,則(0,k+1)=1,繼續檢測(0,k+2)。
  4. 任意需要檢測(0,k+2)且k==m-1的情況,都是非法的。

嗯,就這麼多。

#include <bits/stdc++.h>
using namespace std;

const int maxrow = 11;
const int maxstat = 1<<11;
int h, w;
long long dp[maxrow][maxstat];

inline bool first_ok(int stat)
{
    for (int i = 0; i < w; )
        if (stat & (1<<i))
        {
            if (i == w-1 || !(stat & (1<<(i+1))) )
                return 0;
            i += 2;
        }else ++i;
    return 1;
}

inline bool judge(int a, int b)
{
    for (int i = 0; i < w; )
    {
        if (!(a & (1<<i)))
        {
            if (!(b & (1<<i))) return 0;
            ++i;
        }else
        {
            if (!(b & (1<<i))) ++i;
            else if (i == w-1 || !(( a & (1<<(i+1)) ) && ( b & (1<<(i+1)) )))
                return 0;
            else i += 2;
        }
    }
    return 1;
}

int main()
{
    while (cin >> h >> w)
    {
        if (!h && !w) break;
        if (w > h) swap(w, h);
        int all = 2 << (w-1);
        memset(dp, 0, sizeof dp);
        for (int i = 0; i < all; ++i)
            if (first_ok(i)) dp[0][i] = 1;
        for (int i = 1; i < h; ++i)
            for (int j = 0; j < all; ++j)
                for (int k = 0; k < all; ++k)
                    if (judge(j, k)) dp[i][j] += dp[i-1][k];
        printf("%lld\n", dp[h-1][all-1]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章