1. 程式人生 > >[dp專題-狀態壓縮dp] 51nod 1033

[dp專題-狀態壓縮dp] 51nod 1033

m*n的一個長方形方格中,用一個1*2的骨牌排滿方格。問有多少種不同的排列方法。(n <= 5)

例如:3 * 2的方格,共有3種不同的排法。(由於方案的數量巨大,只輸出 Mod 10^9 + 7 的結果)

Input

2個數M N,中間用空格分隔(2 <= m <= 10^92 <= n <= 5

Output

輸出數量 Mod 10^9 + 7

Input

示例

2 3

Output示例

3

對於n為5的情況:結合程式碼看下圖, dp[i][j]表示的意義是在連續的k長的一段中,首部狀態是i,尾部狀態是j的組成的圖形中,一共有多少種鋪磚方法,這裡的k實際上就是dp=dp*dp運算了k次,可以結合矩陣和圖的聯絡去理解。

在函式dfs中完成對dp的初始化,此時k實際上是1,然後計算出dp^(m+1)的值就行了。

#include <iostream> 
#include <algorithm> 
#include 
<cstring> #include <cstdio> using namespace std; typedef long long ll; const int mod=1e9+7; ll dp[1<<5][1<<5]; int m,n; void dfs(int col,int pre,int now) { if(col>n) return; if(col==n) { dp[pre][now]++; return; } //在這裡沒有做好!為什麼這裡只用三個dfs,是因為如果加上後兩個就會有重複了。
//認真考慮一下還是可以相對的 dfs(col+1,pre<<1,(now<<1)|1); dfs(col+1,(pre<<1)|1,now<<1); dfs(col+2, pre<<2 , now<<2); //dfs(col+2, pre<<2 , (now<<2)|3); //dfs(col+2, (pre<<2)|3 , now<<2); } void mul(ll ret[1<<5][1<<5],ll a[1<<5][1<<5],ll b[1<<5][1<<5]) { for(int i=0; i<(1<<n); i++) for(int j=0; j<(1<<n); j++) { ll tmp=0; for(int k=0; k<(1<<n); k++) { tmp+=a[i][k]*b[k][j]; tmp%=mod; } ret[i][j]=tmp; } } int main() { scanf("%d%d",&m,&n); memset(dp,0,sizeof(dp)); dfs(0,0,0); ll ret[1<<5][1<<5]; ll tmp[1<<5][1<<5]; memset(ret,0,sizeof(ret)); for(int i=0; i<(1<<n); i++) ret[i][i]=1; m++; while(m) { for(int i=0; i<(1<<n); i++) for(int j=0; j<(1<<n); j++) tmp[i][j]=ret[i][j]; if(m&1) { mul(ret,tmp,dp); } m=m>>1; mul(tmp,dp,dp); for(int i=0; i<(1<<n); i++) for(int j=0; j<(1<<n); j++) dp[i][j]=tmp[i][j]; } cout<<ret[0][(1<<n)-1]<<endl; }