關於矩陣乘法優化dp(入門+斐波那契模板題)

矩陣乘法就是指一個a*b的矩陣和一個b*c的矩陣相乘得到一個a*c的矩陣
我們分別叫做A矩陣,B矩陣和C矩陣。
C[i][j]=nk=1a[i][k]+b[k][j]
而用矩陣乘法優化dp時,實際上是一個矩陣自己與自己相乘,所以可以看做是矩陣的冪運算。
而關於冪運算,自然可以用快速冪來優化。

——————————————————————————————————————————–前話到此爲止

我們都知道dp的實質就是遞推
先舉個例子吧,比如用矩陣乘法優化快速求斐波那契數列。
我們都知道斐波那契數列的遞推式爲f[i]=f[i-2]+f[i-1]
我們可以將這個式子轉化成爲一個矩陣A
F[i]    F[i-1]
F[i-1]  F[i-2]
與另一個矩陣B
1   1
1   0
相乘
得出來矩陣C
F[i]+F[i-1]   F[i]
F[i]         F[i-1]
最後實際上就變成了
F[i+1]   F[i]
F[i]     F[i-1]
實際上是i++的原矩陣。
而當矩陣C繼續與B相乘的時候,顯然又會的出F[i+2]相關的矩陣
那麼實際上我們進行的操作就是由一個矩陣
F[3]   F[2]
F[2]   F[1]
與矩陣不斷的相乘。
那麼實際上就只需要求出B矩陣的冪再與A相乘。

關於實現上,我們知道正常的快速冪中res/ans(即記錄返回值的量)的初始值是1
那麼矩陣的快速冪中,記錄返回矩陣的初始矩陣是什麼樣的呢。
經過大量推理(julizi),我發現實際上這個矩陣是一個除了左上→右下對角線爲1,其餘都爲0的矩陣,大家也可以自己推一推(jujulizi),其實也很好理解,畢竟c[i][j]=a[i][k] b[k][j],而由於只有b[j][j]存在值,所以這個式子就變成了c[i][j]=a[i][j] b[j][j],而b[j][j]又是1.

模板題
http://poj.org/problem?id=3070

看到我以上口胡沒看懂的可以自己用代碼print一下矩陣什麼的..不過畢竟入門題沒啥難度

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=4010;
const int INF=1e9+7;
const int mod=10000;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
int fib[]={0,1};
struct kaga
{
    ll v[2][2];
    kaga friend operator *(kaga a,kaga b)
    {
        kaga c;
        fer(i,0,1)
            fer(j,0,1)
            {
                c.v[i][j]=0;
                fer(k,0,1)
                    c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j])%mod;
            }
        return c;
    }
}a,b,c;
void print(kaga a)
{
    fer(i,0,1)
    {
        fer(j,0,1)
        cout<<a.v[i][j]<<" ";
        cout<<endl;
    }
}
void init()
{
    a.v[0][0]=2;a.v[0][1]=a.v[1][0]=a.v[1][1]=1;
    b.v[0][0]=b.v[1][0]=b.v[0][1]=1;b.v[1][1]=0;
    c.v[0][1]=c.v[1][0]=0;c.v[1][1]=c.v[0][0]=1;
    //print(a);
    //print(b);
    //print(c);
}
int main()
{
    int n;
    while(scanf("%d",&n))
    {
        if(n==-1)return 0;
        if(n<=1)
        {
            cout<<fib[n]<<endl;
            continue;
        }
        n--;
        init();
        for(;n;n>>=1,b=b*b)
            if(n&1)c=b*c;
        a=a*c;
        cout<<a.v[1][1]<<endl;
    }
}

(這代碼是真的醜)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章