軟體能力認證題---拼圖(狀態壓縮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.