• 来一波矩阵操作——从高斯消元到矩阵树定理


    一. 高斯消元求线性方程组

    基本思路:

    先从上往下消成一个上三角矩阵
    再从下往上代入求值

    判断解的情况:

    1.出现类似0=1形式:无解
    2.出现0=0形式:多组解
    3.其他情况:一组解

    代码:

    typedef double db[N][N];
    int n;
    
    bool Gauss(db A){
        for(int i=1;i<n;i++){
            int r=i;
            for(int j=i+1;j<=n;j++)
                if(fabs(A[j][i]-A[r][i])>eps) r=j; //为保证精度,选取最大的A[r][i]作除数
            for(int j=i;j<=n+1;j++) swap(A[i][j],A[r][j]);
            if(fabs(A[i][i])<=eps) continue; //可能多解也可能无解
            for(int j=i+1;j<=n;j++){
                double x=A[j][i]/A[i][i];
                for(int k=i;k<=n+1;k++) 
                    A[j][k]-=A[i][k]*x;
            }
        }
        for(int i=n;i>0;i--){
            for(int j=n;j>i;j--) A[i][n+1]-=A[i][j]*A[j][n+1];
            if(fabs(A[i][i])<=eps) return false; //可能多解也可能无解
            A[i][n+1]/=A[i][i];
        }
        return true;
    }
    

    二.矩阵求逆

    定义:

    对于 (n imes n) 的矩阵 (A) , 若存在矩阵 (B) 满足 (A imes B = E)(E) 为单位矩阵),则称 (B)(A) 的逆矩阵

    性质:

    令矩阵 (P=[A|E]) ,对 (P) 进行一些线性变换使之变成 ([E|B]) 的形式,则 (A imes B = E)

    证明:

    首先有一个可证的性质,对矩阵 (M) 进行线性变换 等价于 (M) 左乘一个矩阵 (N) (证明略)
    然后就比较显然了,(A imes B = E,E imes B = B)

    做法:

    先从上往下将 (P)(A) 部分消成上三角矩阵
    再从下往上将 (P)(A) 部分消成单位矩阵

    代码:

    (对大质数取模版)

    #define xzy 1000000007
    
    typedef long long ll;
    typedef int Mat[N][N];
    
    int Pow_mod(int x,int y){
        int ret=1;
        while(y){
            if(y&1) ret=((ll)ret*x)%xzy;
            x=((ll)x*x)%xzy;
            y>>=1;
        }
        return ret;
    }
    bool Gauss_inv(Mat A,int n,int m){ // m=2n
        for(int i=1;i<=n;i++){
            int r=i;
            for(int j=i+1;j<=n;j++)
                if(A[j][i]>A[r][i]) r=j;
            for(int j=i;j<=m;j++) swap(A[i][j],A[r][j]);
            if(!A[i][i]) return false;
            int y=Pow_mod(A[i][i],xzy-2);
            for(int j=i+1;j<=n;j++){
                int x=(ll)A[j][i]*y%xzy;
                for(int k=i;k<=m;k++)
                    A[j][k]=(A[j][k]-(ll)A[i][k]*x%xzy+xzy)%xzy;
            } 
        }
        for(int i=n;i>0;i--){
            for(int j=i+1;j<=n;j++){
                for(int k=n+1;k<=m;k++)
                    A[i][k]=(A[i][k]-(ll)A[i][j]*A[j][k]%xzy+xzy)%xzy;
                A[i][j]=0;
            }
            int x=Pow_mod(A[i][i],xzy-2);
            for(int j=n+1;j<=m;j++) A[i][j]=((ll)A[i][j]*x)%xzy;
            A[i][i]=1;
        }
        return true;
    }
    

    三.矩阵行列式

    定义:

    略(会用就行了)

    重要性质:

    交换行列式的两行,行列式取相反数
    将行列式某一行元素乘以同一个数后加到另一行对应的元素上去,行列式不变
    上三角矩阵或下三角矩阵行列式为正对角线上元素乘积

    代码:

    (对合数取模——利用辗转相除法将 (a,b) 中的一个消为0)

    #define xzy 1000000000
    
    typedef int Mat[N][N];
    typedef long long ll;
    
    int Gauss(Mat A,int n){
        int ret=1;
        for(int j=1;j<=n;j++){
            for(int i=j+1;i<=n;i++)
                while(A[i][j]){ // (a,b) -> (b,a%b) = (b,a-(a/b)*b)
                    int t=A[j][j]/A[i][j]; //A[i][j]相当于b,A[j][j]相当于a
                    for(int k=j;k<=n;k++){
                        A[j][k]=(A[j][k]-(ll)A[i][k]*t%xzy+xzy)%xzy;
                        swap(A[i][k],A[j][k]);
                    }
                    ret*=-1;
                }
            ret=((ll)ret*A[j][j]%xzy+xzy)%xzy;
        }
        return ret;
    }
    

    四.无向图矩阵树定理

    一些定义:

    度数矩阵 (D)

    [D[i][j]= egin{cases} 度数,&i==j \ 0,&i!=j end{cases} ]

    邻接矩阵 (A)

    [A[i][j]= egin{cases} 1,&edge(i,j)=true \ 0,&else end{cases} ]

    基尔霍夫((kirchhoff))矩阵 (K)(K=D-A)
    余子式:一个矩阵 (C) 的余子式 (M[i,j]) 表示 (C) 去掉第 (i) 行与第 (j) 列后得到的矩阵的行列式

    定理内容:

    无向图的生成树数量,等于基尔霍夫矩阵任意余子式 (M[i,i]) (!!!注意是 ("i,i"))的行列式
    如果图不连通,则其任意余子式 (M[i,i]) 的行列式均为0

    代码:

    (模板题:(bzoj4031) ([HEOI2015])(Z) 的房间)

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define xzy 1000000000
    
    using namespace std;
    
    const int N = 85;
    typedef int Mat[N][N];
    typedef long long ll;
    
    int Gauss(Mat A,int n){
        int ret=1;
        for(int j=1;j<=n;j++){
            for(int i=j+1;i<=n;i++)
                while(A[i][j]){
                    int t=A[j][j]/A[i][j];
                    for(int k=j;k<=n;k++){
                        A[j][k]=(A[j][k]-(ll)A[i][k]*t%xzy+xzy)%xzy;
                        swap(A[i][k],A[j][k]);
                    }
                    ret*=-1;
                }
            ret=((ll)ret*A[j][j]%xzy+xzy)%xzy;
        }
        return ret;
    }
    
    Mat a;
    
    char ch[10][10];
    int dir[4][2]={-1,0,1,0,0,-1,0,1};
    int tot,id[10][10];
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%s",ch[i]+1);
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(ch[i][j]=='.') id[i][j]=++tot;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(ch[i][j]=='*') continue;
                for(int k=0;k<4;k++)
                    if(id[i+dir[k][0]][j+dir[k][1]]){
                        a[id[i][j]][id[i][j]]++;
                        a[id[i][j]][id[i+dir[k][0]][j+dir[k][1]]]=-1;
                    }
            }
        
        printf("%d
    ",Gauss(a,tot-1));
        
        return 0;
    }
    

    五.有向图矩阵树定理

    一些定义:

    入度矩阵 (D)

    [D[i][j]= egin{cases} 入度,&i==j \ 0,&i!=j end{cases} ]

    出度矩阵 (D)

    [D[i][j]= egin{cases} 出度,&i==j \ 0,&i!=j end{cases} ]

    邻接矩阵 (A):同上
    内向生成树:生成树上的边为儿子指向父亲
    外向生成树:生成树上的边为父亲指向儿子
    扩展基尔霍夫矩阵(K)(K=D-A)

    定理内容:

    (root) 为根的生成树个数为 (K) 的余子式 (M[root,root]) 的行列式
    其中:
    若为内向生成树,则 (D) 为出度矩阵
    若为外向生成树,则 (D) 为入度矩阵

    代码:

    (模板题:(bzoj5297) ([Cqoi2018]) 社交网络)

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    
    #define xzy 10007
    
    using namespace std;
    
    const int N = 255;
    typedef long long ll;
    typedef int Mat[N][N];
    
    int Gauss(Mat A,int n){
        int ret=1;
        for(int j=1;j<=n;j++){
            for(int i=j+1;i<=n;i++)
                while(A[i][j]){
                    int t=A[j][j]/A[i][j];
                    for(int k=j;k<=n;k++){
                        A[j][k]=(A[j][k]-(ll)A[i][k]*t%xzy+xzy)%xzy;
                        swap(A[i][k],A[j][k]);
                    }
                    ret*=-1;
                }
            ret=((ll)ret*A[j][j]%xzy+xzy)%xzy;
        }
        return ret;
    }
    
    Mat a;
    
    int main()
    {
        int n,m,x,y;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            scanf("%d%d",&x,&y);//y->x
            x--; y--;
            a[x][x]++; a[y][x]--;
        }
        
        printf("%d
    ",Gauss(a,n-1));
        
        return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    centos已经安装了python2,同时安装python3
    linux下判断磁盘是ssd还是hdd
    Java中如何判断两个对象是否相等
    NPOI导出Excel和基于office导出Excel比较
    asp
    又找到了一个blog
    关于宋朝及中国历史的一点想法
    Android中实现EditText圆角
    Python小练习
    软件架构设计(三) 架构、架构师和架构设计
  • 原文地址:https://www.cnblogs.com/lindalee/p/10702908.html
Copyright © 2020-2023  润新知