[hihoCoder] 骨牌覆蓋問題·二


時間限制:10000ms
單點時限:1000ms
內存限制:256MB

描述

上一周我們研究了2xN的骨牌問題,這一周我們不妨加大一下難度,研究一下3xN的骨牌問題?
所以我們的題目是:對於3xN的棋盤,使用1x2的骨牌去覆蓋一共有多少種不同的覆蓋方法呢?
首先我們可以肯定,奇數長度一定是沒有辦法覆蓋的;對於偶數長度,比如2,4,我們有下面幾種覆蓋方式:

 

提示:3xN骨牌覆蓋

輸入

第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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM