用矩阵乘法优化递推
(有關矩陣乘法的基本規則請自行搜索)
引例:求斐波那契數列的第 n 項 mod 1000000007 的值,n <= 1018。
分析:斐波那契數列的遞推式為 f(n) = f(n-1)+f(n-2),直接循環求出 f(n) 的時間復雜度是 O(n),對于題目中的數據范圍顯然無法承受。很明顯我們需要對數級別的算法。
由于 f(n) = 1*f(n-1) + 1*f(n-2) 這樣的形式很類似于矩陣的乘法,所以我們可以先把這個問題復雜化一下,將遞推求解 f(n) 與 f(n-1) 的過程看作是某兩個矩陣相乘的結果,式子如下:
[ f(n-2), f(n-1) ]
*
[ 0, 1 ]
[ 1, 1 ]
=
[ f(n-1), f(n-2)+f(n-1)=f(n) ]
所以我們只要不斷地乘以上面式子中的第二個矩陣(也就是第二個矩陣的冪)就能夠不斷遞推得到 f(n)。但是這樣于解題沒有絲毫益處,反而使得常數變得更大(矩陣乘法的復雜度為立方級別)。所以我們就要利用矩陣乘法的一條重要性質:結合律。即矩陣 (A*B)*C = A*(B*C),證明過程可參見 2008 年國家集訓隊俞華程的論文。
有了結合律我們就可以用快速冪計算矩陣的冪,問題的復雜度順利降到了 O(logn)。
?
例:Sam 數
Sam 數是指相鄰的兩位數字相差不超過 2 的數。求長度為 n 的 Sam 數有多少個。輸出對 1000000007 取余后的結果。n <= 10^18。
分析:這個數據范圍很嚇人,所以必須要 O(logn) 的算法。
首先,遞推式很明顯:
用 f(i, j) 表示第 j 位為 i 時總的個數(先不考慮各種特殊情況),則
f(i, j) = f(i-2, j-1) + f(i-1, j-1) + f(i, j-1) + f(i+1, j-1) + f(i+2, j-1)
我們考慮這樣一個矩陣 A:
[ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
這個 1*10 矩陣表示當第一位為 i 且一共只有 1 位時的方案數。
考慮另一個矩陣 B:
這個矩陣的第 i 行 第 j 列 表示當某一位為 j,相鄰一位取 i 時的方案數。
我們將矩陣 A 與 B 相乘一次,可以得到一個 1*10 的矩陣,很明顯,這個矩陣的第 i 個元素的值表示當第一位為 i 且一共有 2 位時的方案數,那么矩陣的元素和即 n=2 時的結果。
同理,將 A 不斷地與 B 相乘,對于 n,最終得到的矩陣為 A * (B ^ n-1) 的結果,這個矩陣的元素和即最終答案。注意 n=1 的時候特判,這時 0 也算一個方案,所以答案為 10。
轉載于:https://www.cnblogs.com/lsdsjy/p/3923077.html
總結
- 上一篇: 关于CacheLookup一个有趣的问题
- 下一篇: bash的RANDOM变量生成的是真正的