描述
骨牌,一種古老的玩具。今天我們要研究的是骨牌的覆蓋問題:
我們有乙個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塊,都...