1. 程式人生 > >[矩陣乘法][DP]Vijos1067 Warcraft III 守望者的煩惱

[矩陣乘法][DP]Vijos1067 Warcraft III 守望者的煩惱

ply int eof ++ multi truct sin struct vector

題目梗概

n個單位的路程,主角每次最多可以走k個單位(也就是每次可以走1-k個單位),問最後到第n個監獄的方法數。

思考

DP轉移方程並不難推導:

dp[i]表示第i個監獄的方法數

$dp\left [ i \right ] = dp\left [ i-1 \right ] + dp\left [ i-2 \right ]\cdots \cdots + dp\left [ i-k-1 \right ]$

但是這個n有點太大了,所以我們需要對DP方程進行優化。

仔細觀察轉移方程會發現,每次都是加上 上一個,減去上一個的末尾。 所以這種形式我們就可以用矩陣來進行優化了。

#include <iostream>
#include 
<cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> using namespace std; const int N = 25; const int MOD = 7777777; typedef long long LL; int t,m,n,k; struct Mat { LL s[12][12]; }a,b; Mat multiply(Mat x, Mat y) { Mat c; memset(c.s,
0,sizeof(c.s)); for(int i = 0;i < k; ++i) { for(int j = 0;j < k; ++j) { for(int z = 0; z < k; ++z) { c.s[i][j] += x.s[i][z] * y.s[z][j]; c.s[i][j] %= MOD; } } }
return c; } void init() { memset(a.s,0,sizeof(a.s)); memset(b.s,0,sizeof(b.s)); //這裏看不懂的看我下面的圖片 a.s[0][0] = 1; for(int i = 1; i < k; ++i) { a.s[i][i-1] = 1; a.s[0][i] = 1; } for(int i = 0; i < k;i++) b.s[i][0] = 1; } LL calc(){ while(n)//矩陣快速冪 { if(n & 1) b = multiply(b,a); a = multiply(a,a); n >>= 1; } return b.s[0][0]; } int main() { while(~scanf("%d%d",&k,&n)) { init(); LL ans = calc(); printf("%lld\n",ans); } }

首先看我這個Tex,我們假定k是2,建立一個2*2的矩陣,就拿樣例說吧。

n=1 技術分享

n=2 技術分享

n=3 技術分享

n=4 技術分享

這個2*2的矩陣是輔助作用,它有什麽作用呢?首先按照矩陣乘法,產生的新矩陣的第一行儲存的是當前dp[i]的值,第二行儲存的是是dp[i-1]的值,用矩陣相乘的形式,來計算DP方程。但是時間復雜度O(n),那還不是等於沒優化嗎?

這時候我們就可以使用矩陣快速冪(時間復雜度${log_{2}}^{n}$),矩陣快速冪不懂的去百度教程。

再簡述一下為什麽矩陣快速冪是可行的:

因為矩陣乘法具有結合律 A*B*C=A*C*B

所以$A\times B^{n} = A\times B^{n-2}\times B^{2}=A \times B^{2} \times B^{n-2}$

[矩陣乘法][DP]Vijos1067 Warcraft III 守望者的煩惱