poj3311 動態規劃 輪廓線 或者狀態壓縮

題目大致意思就是給你1*2的矩形磚塊 讓你填充滿h*w的矩形區域 有多少種填充方法?hw的值小於12,
兩種解法:
1. 狀態壓縮dp
思路很簡單,對於每一個格子,我們考慮這個格子上面我們是豎向放磚塊還是橫向放磚塊,如果橫向放 我們就有橫着的兩個11表示,豎向放我們就用豎着的01表示,那麼每一行就可以表示爲一個狀態,在其中一行的時候,首先判斷當前行的狀態是否符合上一行的要求,即上一行爲0的地方這一行這個位置必須是1,上一行爲1的地方這一行這個位置就可以隨便,然後檢查一下當前行是否合法就可以了,合法指的是這一行與上一行的值按位與,得到的值的二進制中的連續1的個數都是偶數個。具體看代碼

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
bool isRight[1<<13];
bool isOk(int s)
{
    int cnt = 0;
    while(s)
    {
        if(s&1)
        {
            cnt++;
        }
        else
        {
            if(cnt&1)return false;
            cnt = 0;
        }
        s>>=1;
    }
    if(cnt&1)return false;
    return true;
}

int main()
{
    memset(isRight,false,sizeof(isRight));
    for(int i = 0;i<1<<13;i++)
    {
        if(isOk(i))
        {
            isRight[i] = true;
        }
    }
    int h,w;
    while(scanf("%d%d",&h,&w) && h && w)
    {
        LL dp[15][1<<13];
        memset(dp,0,sizeof(dp));
        for(int i = 0;i<(1<<w);i++)
        {
            if(isRight[i])
            {
                dp[0][i] = 1;
            }
        }
        for(int i = 1;i<h;i++)
        {
            for(int j = 0;j<(1<<w);j++)
            {
                for(int k = 0;k<(1<<w);k++)
                {
                    int nk = ((1<<w)-1)^k;
                    if((nk&j) != nk)continue;
                    // 上面兩行代碼是判斷當前狀態是否符合上面一行的要求 用位運算 上一行取反 然後異或當前行判斷是否相等 判斷
                    if(isRight[j&k])
                    {
                        dp[i][j] += dp[i-1][k];
                    }
                }
            }
        }
        //cout << isRight[7] << endl;
        printf("%lld\n",dp[h-1][(1<<w)-1]);
    }
    return 0;
}
  1. 輪廓線動態規劃
    這裏有一個非常好的題解
    簡單的說一下自己的理解吧,輪廓線的的轉移類似一個蛇在圖上移動,每次用下一個格子和上一個輪廓線(蛇)來轉移,在新的格子有兩種放放法,例如以題解中的o格 可以放磚在K4O上 即上放,或者k0O即左放
    代碼:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long

int main()
{
    int w,h;
    while(scanf("%d%d",&h,&w) && w && h)
    {
        LL dp[15][1<<13];
        memset(dp,0,sizeof(dp));
        dp[0][(1<<w)-1] = 1;
        int cur = 0;
        for(int i = 0;i<h;i++)
        {
            for(int j = 0;j<w;j++)
            {
                for(int k = 0;k<(1<<w);k++)
                {
                    // 不放 直接左移 但是保證上一個輪廓線首位爲1
                    if(k & (1<<w-1))
                        dp[!cur][(k<<1)&(1<<w)-1] += dp[cur][k];
                    // 上放 上一個輪廓線首位爲0不然放不了 
                    if(i && !((1<<(w-1))&k))
                        dp[!cur][(k<<1)|1] += dp[cur][k];
                    // 左放 保證上一個 輪廓線尾部爲0 然後左放
                    if(j && !(k&1) && (k & (1<<w-1)))
                    {
                        int id = (((k|1)<<1)|1) & (1<<w)-1;
                        dp[!cur][id] += dp[cur][k];
                    }
                }

                memset(dp[cur],0,sizeof(dp[cur]));
                cur = !cur;
            }
        }
        printf("%lld\n",dp[cur][(1<<w)-1]);
    }
    return 0;
}

兩個代碼時間差別非常大 狀態壓縮用了579ms 輪廓線用了79ms

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章