軟體能力認證題---拼圖(狀態壓縮DP+矩陣快速冪)

來源:互聯網
上載者:User

標籤:狀態壓縮   動態規劃   矩陣快速冪   

題意: 給定n*m的棋盤(1<=N<=10^15, 1<=M<=7),用L型骨牌(田字型任意去掉一個口)完全覆蓋它,問有多少種解。

思路:m的範圍只有1<=M<=7,顯然狀壓DP。但是N的最大值到10^15,只能用快速冪了。

狀態表示:0代表此處留空,1代表此處被填滿。01序列壓縮成一個int型來表示一行的填放情況。(例如:狀態為4,則代表100,即第一列填滿,第二第列三空)

遞推矩陣是長這樣的:

邊界條件:

其中,

t = 2^M

代表將前i-1行填滿,且第i行放置了狀態s時的總方案數。

代表上一行原本放置了狀態s2的前提下,當前行放置骨牌把上一行填滿並使得當前行狀態為s1的方案數。


那麼問題來了,怎麼取得矩陣D呢?

我枚舉上一行狀態s2,對每個s2進行dfs:依次掃描s2的每一位,如果有空位置,則嘗試拜訪某個方向的骨牌(顯然共4種方向)。當把s2所有位置拜滿以後,s1便產生了,那麼讓增加1 (初始為0).


有了矩陣D,天黑都不怕!

顯然有


矩陣快速冪即可,最後輸出就是結果。

題目本身沒多難,都怪我裝壓DP沒學好,做了這題總算弄明白了一些誤區。

明明還有好多事情要忙,但是看到這種東西就是想做,好久沒有這種純粹的感覺了。。


下面附上代碼

#include<cstdio>#include<cstring>#include<iostream>using namespace std;typedef long long LL;const int mod = 1000000007;const int maxn = 130;int off[5]={0,1,1,2,2};int d[maxn][maxn];LL N;int M; //N行 M列int maxs; //總狀態數 1<<Minline void int2arr(int num,bool arr[])//數字轉矩陣{    for(int i=0;i<M;++i,num>>=1)        arr[M-i-1] = num&1;}inline int arr2int(bool arr[])//矩陣轉數字{    int num = 0;    for(int i=0;i<M;(num<<=1)|=arr[i++]);    return num;}bool sbuf[2][10];inline bool check(int cur,int type)//判斷在cur位置是否可以放置type方向的骨牌{    if(type == 1)        return sbuf[0][cur]==0 && sbuf[1][cur]==0 && cur+1<M && sbuf[1][cur+1]==0;    if(type == 2)        return sbuf[0][cur]==0 && sbuf[1][cur]==0 && cur-1>=0 && sbuf[1][cur-1]==0;    if(type == 3)        return sbuf[0][cur]==0 && sbuf[1][cur]==0 && cur+1<M && sbuf[0][cur+1]==0;    if(type == 4)        return sbuf[0][cur]==0 && cur+1<M && sbuf[0][cur+1]==0 && sbuf[1][cur+1]==0;}inline void putblock(int cur,int type,int cont)//在cur位置放置type方向的骨牌{    if(type == 1)        sbuf[1][cur] = sbuf[1][cur+1] = cont;    if(type == 2)        sbuf[1][cur] = sbuf[1][cur-1] = cont;    if(type == 3)        sbuf[1][cur] = cont;    if(type == 4)        sbuf[1][cur+1] = cont;}void dfs(int cur)//dfs不多解釋{    if(cur >= M)    {        ++d[arr2int(sbuf[1])][arr2int(sbuf[0])];        return;    }    if(sbuf[0][cur]==1) dfs(cur+1);//如果當前位置已填,繼續嘗試下一列    for(int i=1;i<=4;++i)        if(check(cur,i))//如果當前位置可以放置i方向骨牌        {            putblock(cur,i,1);//嘗試放置i方向骨牌            dfs(cur+off[i]);//繼續嘗試後面列            putblock(cur,i,0);//撤銷放置i方向骨牌        }}inline void getd()//計算矩陣D{    maxs = 1<<M;    memset(d,0,sizeof(d));    for(int i=0;i<maxs;++i)            int2arr(i,sbuf[0]),            dfs(0);}inline void matmult(int a[maxn][maxn],int b[maxn][maxn])//矩陣乘法a*=b{    static int c[maxn][maxn];    memset(c,0,sizeof(c));    for(int i=0;i<maxs;++i)        for(int j=0;j<maxs;++j)            for(int k=0;k<maxs;++k)                c[i][j] = (c[i][j] + ((LL)a[i][k] * (LL)b[k][j]) % mod) % mod;    memcpy(a,c,sizeof(c));}void matpower(int a[maxn][maxn],LL p)//矩陣快速冪a^p{    int ans[maxn][maxn];    memset(ans,0,sizeof(ans));    for(int i=0;i<maxs;++i) ans[i][i]=1;    //ans=1    for(;p;p>>=1,matmult(a,a))        if(p&1)            matmult(ans,a);//ans*=a;       //a*=a;    memcpy(a,ans,sizeof(ans)); //return ans}int main(){    cin>>N>>M;    getd();    matpower(d,N);    cout<< d[ maxs-1 ][ maxs-1 ] <<endl;    return 0;}/*各個方向的骨牌及其對應編號 1  *    ** 2  *   ** 3  **    * 4  **     **/

軟體能力認證題---拼圖(狀態壓縮DP+矩陣快速冪)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.