描述
上一周我們研究了2xN的骨牌問題,這一周我們不妨加大一下難度,研究一下3xN的骨牌問題?
所以我們的題目是:對於3xN的棋盤,使用1x2的骨牌去覆蓋一共有多少種不同的覆蓋方法呢?
首先我們可以肯定,奇數長度一定是沒有辦法覆蓋的;對於偶數長度,比如2,4,我們有下面幾種覆蓋方式:
輸入
第1行:1個整數N。表示棋盤長度。1≤N≤100,000,000
輸出
第1行:1個整數,表示覆蓋方案數 MOD 12357
- 樣例輸入
-
62247088
- 樣例輸出
-
4037
在2xN的骨牌覆蓋問題中,我們有遞推式子 (0,1)xM^n=(f[n-1],f[n])。
我們考慮能否在3xN的情況下找到同樣的式子。
但在實際的推導過程可以發現,對於3xN的覆蓋,對應的f數值公式比2xN復雜太多。我們需要換個角度來思考推導公式。
在我們放置骨牌的過程中,一定是放好一行之后再放置下一行。根據擺放的方式,可能會產生很多種不同的形狀,而這些形狀之間是否具有某些遞推關系呢?
如果他們存在一定的遞推關系,則我們可以根據第i行的方案數來推導第i+1行的方案數。這樣一行一行推導,直到第N行時不就得到了我們要求的方案數了么?
那么來研究一下是否存在這樣的推導公式吧
假設我們已經放好了一些骨牌,對於當前最后一列(第i列)骨牌,可能有8種情況:
對於上面這8種狀態,我們用數字來標記它們。以有放置骨牌的格子為1,未放置為0,轉化為2進制數
以最下面一行作為1,則有:
接下來考慮如何放置骨牌,我們先將棋盤旋轉一下。假設我們正在放置第i行的骨牌,那么會有下面3種方式:
灰色表示已經有的骨牌,綠色表示新放置的骨牌。
每一種放置方法解釋如下,假設當第i行的狀態為x,第i-1行的狀態為y:
- 第i行不放置,則前一行必須有放置的骨牌。x對應二進制位為0,y對應二進制位為1。
- 第i行豎放骨牌,則前一行必須為空。x對應二進制位為1,y對應二進制位為0。
- 第i行橫向骨牌,則前一行必須兩個位置均有骨牌,否則會產生空位。x對應二進制位為1,y對應二進制位為1。
舉個例子:
對於第i行狀態1,我們在第i+1行豎放兩塊骨牌之后便能到達狀態6。
但是在這之中需要注意會出現下面這種情況:
這種情況看似是從狀態1變成了狀態0,其實是不對的。它不滿足我們約定的放置方法,本質是第i行的狀態1變成了第i行的狀態7,而實際上我們應該放置的是第i+1行。
所以在枚舉遞推關系的時候一定要注意。
通過枚舉8種狀態到8種狀態的轉移,我們可以得到一個8x8的矩陣M(空白的地方均為0):
m[i][j]表示從狀態i變成狀態j的方案數。
現在我們有了M矩陣,接下來考慮邊界情況。
在2xN的骨牌覆蓋中,有(0, 1)作為初始向量A,那么在3xN中初始向量A是如何呢?
讓我們先想想A向量所代表的含義。M矩陣表示狀態到狀態的轉移,則A向量所表示的應該就是第0行各狀態的方案數。
同理,對於A * M^n所求出的結果則應該表示為第n行各種狀態的方案數。
那么A向量應該是多少呢?很顯然,第0行在我們遞推的過程中必須看作狀態7才合理。故A向量表示為:
{0, 0, 0, 0, 0, 0, 0, 1}
而對於我們尋求的答案,自然也是第n行放置為狀態7的方案數了。
____________________________
其實仔細想想畫一畫也可以得到遞推公式,假設奇數的方案數不為0,只要有一個方塊達到奇數長度,就算是其中一個方案,那么有:
a[0] = 0; a[1] = 2; a[2] = 3;
對於奇數:a[i] = 2*a[i-1] + a[i-2]; 對於偶數:a[i] = 3*a[i-2] + a[i-3];
為了節省空間可以用循環數組還存儲結果。下面是AC的代碼。
1 #include <iostream> 2 using namespace std; 3 4 typedef unsigned long long ll; 5 const ll MOD = 12357; 6 7 ll N; 8 ll a[5]; 9 10 void solve() { 11 a[0] = 0; 12 a[1] = 2; 13 a[2] = 3; 14 for (int i = 3; i <= N; ++i) { 15 if (i & 1) { 16 a[i%5] = (2*a[(i-1+5)%5] + a[(i-2+5)%5]) % MOD; 17 } else { 18 a[i%5] = (3*a[(i-2+5)%5] + a[(i-3+5)%5]) % MOD; 19 } 20 } 21 cout << a[N%5] << endl; 22 } 23 24 int main() { 25 while (cin >> N) { 26 if (N & 1) { 27 cout << "0" << endl; 28 } else { 29 solve(); 30 } 31 } 32 return 0; 33 }