2015程式設計之美 骨牌覆蓋問題 一(矩陣快速冪)

2021-07-01 21:36:16 字數 1965 閱讀 5017

描述

骨牌,一種古老的玩具。今天我們要研究的是骨牌的覆蓋問題:

我們有乙個2xn的長條形棋盤,然後用1x2的骨牌去覆蓋整個棋盤。對於這個棋盤,一共有多少種不同的覆蓋方法呢?

舉個例子,對於長度為1到3的棋盤,我們有下面幾種覆蓋方式:

輸入第1行:1個整數n。表示棋盤長度。1≤n≤100,000,000

輸出第1行:1個整數,表示覆蓋方案數 mod 19999997

樣例輸入

62247088
樣例輸出

17748018
我們考慮在已經放置了部分骨牌(灰色)的情況下,下一步可以如何放置新的骨牌(藍色):

最右邊的一種情況是不可能發生的,否則會始終多乙個格仔沒有辦法放置骨牌。或者說灰色部分的格仔數為奇數,不可能通過1x2個骨牌放置出來。

那麼通過對上面的觀察,我們可以發現:

在任何乙個放置方案最後,一定滿足前面兩種情況。而灰色的部分又正好對應了長度為n-1和n-2時的放置方案。由此,我們可以得到遞推公式:

f[n] = f[n-1] + f[n-2];

這個公式是不是看上去很眼熟?沒錯,這正是我們的費波拉契數列。

f[0]=1,f[1]=1,f[2]=2,...

當n很小的時候,我們直接通過遞推公式便可以計算。當n很大的時候,只要我們的電腦足夠好,我們仍然可以直接通過遞推公式來計算。

但是我們學演算法的,總是這樣直接列舉不是顯得很low麼,所以我們要用乙個好的演算法來加速(裝x)。

事實上,對於這種線性遞推式,我們可以用矩陣乘法來求第n項。對於本題fibonacci數列,我們希望找到乙個2x2的矩陣m,使得(a, b) x m = (b, a+b),其中(a, b)和(b, a+b)都是1x2的矩陣。

顯然,只需要取m = [0, 1; 1, 1]就可以了:

進一步得到:

那麼接下來的問題是,能不能快速的計算出m^n?我們先來分析一下冪運算。由於乘法是滿足結合律的,所以我們有:

不妨將k[1]..k[j]劃分的更好一點?

其中(k[1],k[2]...k[j])2表示將n表示成二進位制數後每一位的數字。上面這個公式同時滿足這樣乙個性質:

結合這兩者我們可以得到乙個演算法:

1. 先計算出所有的,因為該數列滿足遞推公式,時間複雜度為o(logn)

2. 將指數n二進位製化,再利用公式將對應的a^j相乘計算出a^n,時間複雜度仍然為o(logn)

則總的時間複雜度為o(logn)

這種演算法因為能夠在很短時間內求出冪,我們稱之為「快速冪」演算法。

#include #include using namespace std;

const int mod=19999997;

struct node

;node mul(node a,node b,int n,int p,int m)

return res;

}int main()

{    int n;

cin>>n;

node a,b;

a.a[0][0]=0;a.a[0][1]=1;a.a[1][0]=1;a.a[1][1]=1;

b.a[0][0]=0;b.a[0][1]=1;

node ans=mm(a,n);

ans=mul(b,ans,1,2,2);

cout<

2015程式設計之美 骨牌覆蓋問題 二 快速冪

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

程式設計之美2015初賽A

時間限制 2000ms 單點時限 1000ms 記憶體限制 256mb 兩個數a和 b a第一行為乙個數t,為資料組數。之後每組資料報含兩行。第一行為n,為集合s的大小。第二行為n個整數,表示集合內的數。對於每組資料輸出一行,形如 case x y x為資料編號,從1開始,y為最大的子集的大小。1 ...

程式設計之美 4 2瓷磚覆蓋地板問題

問題 原有地板鋪有 nxm 塊正方形瓷磚老化了需要更新,但商店已經沒有此類瓷磚了,只 長方形的瓷磚,分析如何用 1x2 的瓷磚去覆蓋 nxm 的地板呢?分析 1.n 1,m為偶數時,需要m 2塊瓷磚 2.若nxm為奇數,則肯定不能用1x2的瓷磚去全部覆蓋它。例如3x3只能放4塊,3x5只能放7塊,都...