zoukankan      html  css  js  c++  java
  • 矩阵相关

    矩阵前置芝士

    矩阵:看成一个二维数组就好了.

    矩阵加法:

    本人认为没用
    条件:两个大小相同的矩阵
    对应位置相加,形成相同大小的矩阵.

    矩阵减法:

    本人认为没用
    条件:两个大小相同的矩阵

    对应位置相减,形成相同大小的矩阵.

    矩阵乘法:

    这个很重要
    条件:两个大小不完全一样的矩阵,必须保证矩阵(A(n*k)),矩阵B大小是(k*m)的,形成的矩阵C大小是(n*m)
    C矩阵中的x,y位置是A矩阵的X行,B矩阵的Y列相加.
    代码:

    for(int i = 1;i <= n;++ i) {
        for(int j = 1;j <= m;++ j) {
            for(int l = 1;l <= k;++ l) 
                C[i][j] += A[i][k] + B[k][j];
        }
    }
    

    性质:
    矩阵乘法具有结合律:
    (A*B*C = (A*B)*C = A*(B*C))

    矩阵快速幂

    ps:使用矩阵快速幂的时候一定要有模数.
    根据矩阵乘法具有结合律的性质,我们可以做矩阵的快速幂:
    例题---Luogu矩阵快速幂
    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define ll long long
    const int mod = 1e9 + 7;
    const int X = 110;
    
    ll n,k;
    
    struct Node {
        ll a[X][X];
        void clear() {
            memset(a,0,sizeof(a));
        }
    }res,ans;
    
    Node mul(Node a,Node b) {
        Node tmp;
        tmp.clear();
        for(int i = 1;i <= n;++ i) {
            for(int j = 1;j <= n;++ j) {
                for(int k = 1;k <= n;++ k) {
                    tmp.a[i][j] = (tmp.a[i][j] + (a.a[i][k] * b.a[k][j]) % mod ) % mod;
                } 
            } 
        }
        return tmp;
    }
    
    void fast_pow(ll k) {
        for(int i = 1;i <= n;++ i)
            ans.a[i][i] = 1;
        for(;k;k >>= 1,res = mul(res,res)) {
            if(k & 1)ans = mul(ans,res);
        }
        return ;
    }
    
    int main() {
        scanf("%lld%lld",&n,&k);
        for(int i = 1;i <= n; ++i ) {
            for(int j = 1;j <= n;++ j) {
                scanf("%lld",&res.a[i][j]);
            }
        }
        ans.clear();
        fast_pow(k);
        for(int i = 1;i <= n;++ i) {
            for(int j = 1;j <= n;++ j) {
                printf("%lld ",ans.a[i][j]);
            }
            printf("
    ");
        }
        return 0;
    }
    
    /*
    5
    5
    1 1 1 1 1
    1 0 0 1 0
    0 0 2 3 1
    0 0 0 1 0
    5 5 5 5 5
    */
    

    斐波那契数列

    我们可以通过矩阵快速幂来优化斐波那契数列斐波那契数列:$$f[n] = f[n - 1] + f[n - 2]$$
    放道例题:
    斐波那契数列---Luogu1962
    如何做:
    斐波那契数列的通项公式???
    显然不行,有精度问题.
    那我们来做矩阵快速幂
    题目中有f[1] = 1,f[2] = 1
    先构造矩阵(先不用管为什么这样构造):

    [ left{ egin{matrix} 1 & 1 \ end{matrix} ight} ag{1} ]

    表示

    [ left{ egin{matrix} 斐波那契数列第二项 & 斐波那契数列第一项 \ end{matrix} ight} ag{2} ]

    尝试往后推一项
    我们想要矩阵成这个样子

    [ left{ egin{matrix} 斐波那契数列第三项 & 斐波那契数列第二项 \ end{matrix} ight} ag{2} ]

    那我们要乘一个矩阵
    首先根据矩阵的定义,我们必须要构造一个(2*2)的矩阵
    首先给出构造好的矩阵:

    [ left{ egin{matrix} 1 & 1\ 1 & 0 \ end{matrix} ight} ag{3} ]

    模拟一下就好了.
    构造就是 根据生成的矩阵来构造中间的这个矩阵
    我们再回过头来看一下,为什么刚开始的矩阵是

    [ left{ egin{matrix} 1 & 1 \ end{matrix} ight} ag{1} ]

    因为f[3]要用到这两项啊.
    记住,矩阵快速幂中间的矩阵不可改变.
    这样我们就可以进行中间的矩阵(n -2)次幂的运算来获得一个矩阵啦,
    然后用刚开始的矩阵与这个矩阵相乘就得到第n项了.
    注意特判(n == 1,n == 2)的特殊情况

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define ll long long
    const int maxN = 100 + 7;
    const ll mod = 1000000007;
    using namespace std;
    
    ll n,m,k;
    struct Marix{
        ll s[maxN][maxN];
        void clear() {
            memset(s,0,sizeof(s));
            return;
        }
    }ans,now,a;
    
    Marix work(Marix a,Marix b) {
        Marix c;
        c.clear();
        for(ll i = 1;i <= n;++ i) 
            for(ll j = 1;j <= n;++ j) 
                for(ll k = 1;k <= n;++ k) 
                    c.s[i][j] = ( c.s[i][j]  % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
        return c;
    }
    
    void fast_pow() {
        ans.clear();
        for(ll i = 1;i <= n;++ i) {
            ans.s[i][i] = 1;
        }
        for(now = a;k;k >>= 1,now = work(now,now)) {
            if(k & 1) ans = work(ans,now);
        }
        Marix Q;
        Q.s[1][1] = 1;Q.s[1][2] = 1;
        ans = work(Q,ans);
        printf("%lld",ans.s[1][1]);
    }
    
    int main() {
        n = 2;
        scanf("%lld",&k);
        if(k == 1) {
        	printf("1");
        	return 0;
        }
        if(k == 2) {
            printf("1");
            return 0;
        }
        k -= 2;
        a.s[1][1] = 1;a.s[1][2] = 1;
        a.s[2][1] = 1;a.s[2][2] = 0;
        fast_pow();
        return 0;
    }
    

    矩阵递推相关

    给道例题:Luogu1939
    建议自己先构造一下:
    开始矩阵:

    [ left{ egin{matrix} 1 (第一项)& 1(第二项) & 1(第三项)\ end{matrix} ight} ag{1} ]

    中间的矩阵:

    [ left{ egin{matrix} 0 & 0 & 1\ 1 & 0 &0 \ 0 & 1 & 1\ end{matrix} ight} ag{1} ]

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define ll long long
    const int maxN = 100 + 7;
    const int mod = 1e9 + 7;
    using namespace std;
    
    ll n,m,k;
    struct Marix{
        ll s[maxN][maxN];
        void clear() {
            memset(s,0,sizeof(s));
            return;
        }
    }ans,now,a;
    
    Marix work(Marix a,Marix b) {
        Marix c;
        c.clear();
        for(ll i = 1;i <= n;++ i) 
            for(ll j = 1;j <= n;++ j) 
                for(ll k = 1;k <= n;++ k) 
                    c.s[i][j] = ( c.s[i][j]  % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
        return c;
    }
    
    void fast_pow() {
        ans.clear();
        for(ll i = 1;i <= n;++ i) {
            ans.s[i][i] = 1;
        }
        for(now = a;k;k >>= 1,now = work(now,now)) {
            if(k & 1) ans = work(ans,now);
        }
        Marix Q;
        Q.s[1][1] = 1;Q.s[1][2] = 1;Q.s[1][3] = 1;
        ans = work(Q,ans);
        printf("%lld
    ",ans.s[1][3]);
    }
    
    int main() {
        n = 3;
        int T;
        scanf("%d",&T);
        while(T --) {
            a.s[1][3] = a.s[2][1] = a.s[3][2] = a.s[3][3] = 1;
            scanf("%lld",&k);
            k -= 3;
            if(k <= 0) printf("1
    ");
            else fast_pow();
        }
        return 0;
    }
    

    矩阵快速幂优化DP

    设置状态f[i][j][k]表示i到j走k步的方案数
    设f[i][j][1] = 0/1表示i到j有没有路(0代表无路,1代表有)
    枚举中间点Q
    (f[i][j][k] = sum_{Q=1}^{n}f[i][Q][k - 1] * f[Q][j][1])
    HDU2157

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    const int maxN = 20 + 7;
    const int mod = 1000;
    using namespace std;
    
    struct Matrix
    {
    	int s[maxN][maxN];
    	Matrix() {memset(s,0,sizeof(s));}
    };
    
    int n,m;
    
    Matrix operator * (const Matrix &a,const Matrix &b) {
    	Matrix c;
    	for(int i = 1;i <= n;++ i) {
    		for(int j = 1;j <= n;++ j) {
    			for(int k = 1;k <= n;++ k) {
    				c.s[i][j] = (c.s[i][j] + a.s[i][k] * b.s[k][j] % mod) % mod;
    			}
    		}
    	}
    	return c;
    }
    
    Matrix fast_pow(Matrix a,int nn) {
    	Matrix ans;
    	for (int i = 1;i <= n; ++i)
    		ans.s[i][i] = 1;
    	for (; nn; nn >>= 1,a = a * a)
    	{
    		if(nn & 1) ans = ans * a;
    	}
    	return ans;
    }
    
    int main()
    {
    	while(~scanf("%d%d",&n,&m) && (n || m)) {
    		Matrix q;
    		for(int i = 1,u,v;i <= m;++ i) {
    			scanf("%d%d",&u,&v);
    			q.s[u + 1][v + 1] = 1;
    		}	
    		int T;
    		scanf("%d",&T);
    		int s,t,k;
    		while(T --) {
    			scanf("%d%d%d",&s,&t,&k);
    			Matrix b;
    			b = fast_pow(q,k);
    			printf("%d
    ", b.s[s + 1][t + 1]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    微信 token ticket jsapi_ticket access_token 获取 getAccessToken get_jsapi_ticket方法
    PHP 日志 记录 函数 支持 数组 对象 新浪 sae 环境 去掉 空格 换行 格式化 输出 数组转字符串
    原生 原始 PHP连接MySQL 代码 参考mysqli pdo
    PHP 数字金额转换成中文大写金额的函数 数字转中文
    使用PHPMailer发送带附件并支持HTML内容的邮件
    设置输出编码格式 header 重定向 执行时间 set_time_limit 错误 报告 级别 error_reporting
    html5 bootstrap pannel table 协议 公告 声明 文书 模板
    指向指针的指针
    二级指针
    c语言:当指针成为参数后
  • 原文地址:https://www.cnblogs.com/tpgzy/p/9535017.html
Copyright © 2011-2022 走看看