zoukankan      html  css  js  c++  java
  • 【矩阵】矩阵初级

    百度百科

    Definition

      矩阵是一个按照长方阵列排列的复数或实数集合。

      描述一个矩阵的形状使用两个参数,为矩阵的行数和列数。在代码中是用r和c体现。

      约定在描述矩阵时,行在前列在后,例如一个n*m的矩阵意味着一个n行m列的矩阵。

      两个矩阵相同,当且仅当他们行、列数相同并且元素完全相同。

      两个矩阵形状相同,当且仅当他们行、列数相同。

    operation algorithm

     addition

      两个矩阵能够进行加法,当且仅当两个矩阵形状相同。

      两个矩阵进行加法,返回一个矩阵,矩阵行、列数与原矩阵相同,对应位置上的值为两个矩阵对应位置上值的和。例如:

           

     subtraction

      类比两个矩阵进行加法,两个矩阵能进行减法,当且仅当他们形状相同。

      两个矩阵进行减法,返回一个矩阵,矩阵行、列数与原矩阵相同,对应位置上的值为两个矩阵对应位置上值的差。例如:

           

     multiplication

      两个矩阵A,B能进行A*B的矩阵乘法,当且仅当A的列数等于B的行数。

      注意,A*B ≠ B*A

      下面约定矩阵A为乘号前的矩阵,矩阵B为乘号后的矩阵。

      两个矩阵A,B进行乘法A*B,返回一个矩阵C,矩阵行数为A的行数;矩阵列数为B的列数。矩阵第i行第j列的值C(i,j)被定义如下:

        C(i,j)A.ck=1 A(i,k)*B(k,j)   。其中A.c代表A的列数。

      例如:

           

     division

      两个矩阵做除法A/B,求出B的逆矩阵B-1,则等价于A*B-1。求逆矩阵的方法过于复杂不表。

     lows

       矩阵加、减法具有交换律,结合率。

       矩阵乘法具有结合率。即(A*B)*C=A*(B*C)。不具有交换律。即A*B*C!=A*C*B。

    矩阵快速幂

      方阵:一个矩阵时方阵当且仅当它的行数等于列数

      主对角线:一个方阵的主对角线被定义为从左上角连接到右下角的对角线。

      单位矩阵:一个方阵是单位矩阵当且仅当它有且仅有主对角线上元素的值为1,其他值为0。

      性质:任何一个矩阵乘上一个能和他相乘的单位矩阵,得到的矩阵是它本身。

      矩阵快速幂:

        一个矩阵能进行矩阵快速幂当且仅当它是一个方阵。

        矩阵进行快速幂与普通快速幂相同。必须使用循环形式。因为使用递归可能会占用较多资源。

    矩阵优化类递推题目

      需要矩阵优化的递推题目一般递推式中,该位置i的值一般前面第i-k项有关,其中k为若干个常数。例如fibnacci数列:fi=fi-1+fi-2

      这样的题目可以将fi到fi+x连续组成一个矩阵。其中x为k的最大值。设W为一个矩阵,解方程|fi,fi+1,…,fi+x|*W=|fi+1,fi+2,…,fi+x+1|。

      解出W是一个常量矩阵,这样数学归纳易证f1*Wn-1=fn,可以求出第n项的值。

    Codes

      下面是封装了矩阵加、乘两则运算的代码

    struct M {
        static const int Maxsize = 10;
        static const int MODNUM = 1000000007;
        int r,c;
        ll mat[Maxsize][Maxsize];
        void clear() {memset(mat,0,sizeof mat);r=c=0;}
        bool can_mul(const M &_a,const M _b) {
            return _a.c==_b.r;
        }
        bool can_add(const M &_a,const M &_b) {
            return (_a.r==_b.r) and (_a.c==_b.c);
        }
    };
    M operator+(const M &_a,const M &_b) {
        M _ans;_ans.clear();
        for(rg int i=1;i<=_a.r;++i) for(rg int j=1;j<=_a.c;++j) _ans.mat[i][j]=(_a.mat[i][j]+_b.mat[i][j])%M::MODNUM;
        _ans.r=_a.r;_ans.c=_a.c;
        return _ans;
    }
    M operator*(const M &_a,const M &_b) {
        M _ans;_ans.clear();
        for(rg int i=1;i<=_a.r;++i) 
            for(rg int j=1;j<=_b.c;++j)
                for(rg int k=1;k<=_a.c;++k)
                    _ans.mat[i][j]=(_ans.mat[i][j]+_a.mat[i][k]*_b.mat[k][j])%M::MODNUM;
        _ans.r=_a.r;_ans.c=_b.c;
        return _ans;
    }

     

    Examples

      P3390 【模板】 矩阵快速幂

      Description

        给定n*n的矩阵A,求A^k

      Input

        第一行,n,k

        第2至n+1行,每行n个数,第i+1行第j个数表示矩阵第i行第j列的元素

      Output

        输出A^k

        共n行,每行n个数,第i行第j个数表示矩阵第i行第j列的元素,每个元素模10^9+7

      Sample Input

    2 1
    1 1
    1 1

      Sample Output

    1 1
    1 1

     

      Hint

        n<=100, k<=10^12, |矩阵元素|<=1000

      Soltion

        板子题要啥Solution

      Code

    #include<cstdio>
    #include<cstring>
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    namespace IO {
        char buf[100];
    }
    
    template <typename T>
    inline void qr(T &x) {
        char ch=getchar(),lst=' ';
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst=='-') x=-x;
    }
    
    template <typename T>
    inline void write(T x,const char aft,const bool pt) {
        if(x<0) {putchar('-');x=-x;}
        int top=0;
        do {
            IO::buf[++top]=x%10+'0';
            x/=10;
        }while(x);
        while(top) putchar(IO::buf[top--]);
        if(pt) putchar(aft);
    }
    
    template <typename T>
    inline T mmax(const T _a,const T _b) {if(_b<_a) return _a;return _b;}
    template <typename T>
    inline T mmin(const T _a,const T _b) {if(_a>_b) return _b;return _a;}
    template <typename T>
    inline T mabs(const T _a) {if(_a<0) return -_a;return _a;}
    
    template <typename T>
    inline void mswap(T &_a,T &_b) {
        T _temp=_a;_a=_b;_b=_temp;
    }
    
    const int maxn = 110;
    const int MOD = 1e9+7;
    
    struct M {
        ll mat[maxn][maxn];
        void clear() {
            memset(mat,0,sizeof mat);
        }
    };
    M MU,re,ans;
    
    int n;ll k;
    
    void mul(const M &_a,const M & _b,M &_c) {
        M _temp;_temp.clear();
        for(rg int i=1;i<=n;++i) {
            for(rg int j=1;j<=n;++j) {
                for(rg int k=1;k<=n;++k)
                    _temp.mat[i][j]=(_temp.mat[i][j]+_a.mat[i][k]*_b.mat[k][j]%MOD)%MOD;
            }
        }
        for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) _c.mat[i][j]=_temp.mat[i][j];
    }
    
    int main() {
        qr(n);qr(k);
        for(rg int i=1;i<=n;++i) for(rg int j=1;j<=n;++j) {
            M &now=MU;qr(now.mat[i][j]);
        }
        for(rg int i=1;i<=n;++i) ans.mat[i][i]=1;
        
        while(k) {
            if(k&1) mul(ans,MU,ans);
            mul(MU,MU,MU);
            k>>=1;
        }
        
        for(rg int i=1;i<=n;++i) {
            for(rg int j=1;j<n;++j) write(ans.mat[i][j],' ',true);
            write(ans.mat[i][n],'
    ',true);
        }
    }

        【P1962】 斐波那契数列 

        Description

          求斐波那契数第n项。其中规定f0=0,f1=1

        Input

          一个数,n

        Output

          一个数,答案取模1e9+7。

        Sample Input

    10

        Sample Output

    55

        Hint

          n在long long范围内

        Solution

          因为知道数列中连续两项就可以推出后面所有的数,所以考虑把两个连续的数放到一个矩阵中。

          考虑|fi,fi+1|*X=|fi+1,fi+2|。记A=|fi,fi+1|,B=|fi+1,fi+2|。r代表行数,c代表列数。

          由矩阵乘法的性质可知,A.c=X.r;B.c=X.c。所以X是二行二列矩阵

          设

          根据矩阵的乘法法则,可以列出方程:

          a*fi+c*fi+1=fi+1

          b*fi+d*fi+1=fi+2

          直接利用①两侧相等解出a=0,c=1。

          对于方程②使用递推式解得b=d=1。

          这样矩阵x就被解出。

          最后答案即是|f1,f2|*Xn-1

        Code

    #include<cstdio>
    #include<cstring>
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    namespace IO {
        char buf[100];
    }
    
    template <typename T>
    inline void qr(T &x) {
        char ch=getchar(),lst=' ';
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst=='-') x=-x;
    }
    
    template <typename T>
    inline void write(T x,const char aft,const bool pt) {
        if(x<0) {putchar('-');x=-x;}
        int top=0;
        do {
            IO::buf[++top]=x%10+'0';
            x/=10;
        }while(x);
        while(top) putchar(IO::buf[top--]);
        if(pt) putchar(aft);
    }
    
    template <typename T>
    inline T mmax(const T _a,const T _b) {if(_b<_a) return _a;return _b;}
    template <typename T>
    inline T mmin(const T _a,const T _b) {if(_a>_b) return _b;return _a;}
    template <typename T>
    inline T mabs(const T _a) {if(_a<0) return -_a;return _a;}
    
    template <typename T>
    inline void mswap(T &_a,T &_b) {
        T _temp=_a;_a=_b;_b=_temp;
    }
    
    const int MOD = 1000000007;
    
    struct M {
        static const int Maxsize = 10;
        static const int MODNUM = 1000000007;
        int r,c;
        ll mat[Maxsize][Maxsize];
        void clear() {memset(mat,0,sizeof mat);r=c=0;}
        bool can_mul(const M &_a,const M _b) {
            return _a.c==_b.r;
        }
        bool can_add(const M &_a,const M &_b) {
            return (_a.r==_b.r) and (_a.c==_b.c);
        }
    };
    M operator+(const M &_a,const M &_b) {
        M _ans;_ans.clear();
        for(rg int i=1;i<=_a.r;++i) for(rg int j=1;j<=_a.c;++j) _ans.mat[i][j]=(_a.mat[i][j]+_b.mat[i][j])%M::MODNUM;
        _ans.r=_a.r;_ans.c=_a.c;
        return _ans;
    }
    M operator*(const M &_a,const M &_b) {
        M _ans;_ans.clear();
        for(rg int i=1;i<=_a.r;++i) 
            for(rg int j=1;j<=_b.c;++j)
                for(rg int k=1;k<=_a.c;++k)
                    _ans.mat[i][j]=(_ans.mat[i][j]+_a.mat[i][k]*_b.mat[k][j])%M::MODNUM;
        _ans.r=_a.r;_ans.c=_b.c;
        return _ans;
    }
    M fib,beg,MS;
    
    ll n;
    
    void beginning();
    M pow(ll);
    
    int main() {
        beginning();
        qr(n);
        if(n<3ll) return printf("1
    ")&0;
        M _ans=pow(n-1); fib=beg*_ans;
        write(fib.mat[1][1],'
    ',true);
        return 0;
    }
    
    void beginning() {
        beg.r=1;beg.c=2;
        beg.mat[1][2]=beg.mat[1][1]=1;
        MS.r=MS.c=2;
        MS.mat[1][2]=MS.mat[2][1]=MS.mat[2][2]=1;
    }
    
    M pow(ll x) {
        M _ans;_ans.clear();_ans.r=2;_ans.c=2;
        _ans.mat[1][1]=_ans.mat[2][2]=1;
        while(x) {
            if(x&1) _ans=_ans*MS;
            MS=MS*MS;
            x>>=1;
        }
        return _ans;
    }

    Summary

      这是矩阵中比较基础的部分,事实上,对于循环形式的快速幂,还是要好好掌握滴。

      行列式真tm变态不会不会滚粗了

  • 相关阅读:
    Red5/FMS视频直播带宽计算
    基于NPOI开源框架写的ExcelHelper
    Using C# 4.0 and dynamic to parse JSON
    跟我学MVVM模式开发
    supermap使用代码示例(GIS)
    使用OpenXML将Excel内容读取到DataTable中
    ADO 数据类型转换表
    I don't like Regex...
    将Datatable转Excel少于4笔时汉字乱码4/26
    记录宝宝成长脚印3/31
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9488991.html
Copyright © 2011-2022 走看看