矩阵总结(矩阵若干类型题)

题目源头:http://www.cnblogs.com/frog112111/ 

类型一:多点的多次操作变换

题目:点的变换

链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=298

题意:N个点,对每个点进行M次操作,(N<=10000,M<=1000000)每次操作包括沿X轴翻转,沿Y轴翻转,横纵坐标同时放大,将(x,y)进行旋转,沿(x1,y1)平移。

思路:由于所有操作对于每个点来说影响效果是一样的,所以用矩阵记录下来操作累计下来的总影响再每个点依次进行操作。


最后一个绕原点旋转可以用极坐标x=ρcosθ,y=ρsinθ,来推导,具体不证。

代码:(乘法无取模版)

#define maxn 5
#define maxm 5
using namespace std;
struct Matrix
{
    int n,m;
    double a[maxn][maxm];
    void init()
    {
        n=m=0;
        memset(a,0,sizeof(a));
    }
    Matrix operator +(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]+b.a[i][j];
        return tmp;
    }
    Matrix operator -(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]-b.a[i][j];
        return tmp;
    }
    Matrix operator *(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<b.m; j++)
                tmp.a[i][j]=0;
        }
        for(int i=0; i<n; i++)
            for(int j=0; j<b.m; j++)
                for(int k=0; k<m; k++)
                {
                    tmp.a[i][j]+=a[i][k]*b.a[k][j];
                }
        return tmp;
    }
    void Copy(const Matrix &x)
    {
        n=x.n;
        m=x.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                a[i][j]=x.a[i][j];
    }
};
Matrix M_quick_pow(Matrix m,int k)
{
    Matrix tmp;
    tmp.n=m.n;
    tmp.m=m.m;
    for(int i=0; i<tmp.n; i++)
    {
        for(int j=0; j<tmp.n; j++)
        {
            if(i==j)
                tmp.a[i][j]=1;
            else tmp.a[i][j]=0;
        }
    }
    while(k)
    {
        if(k&1)
            tmp.Copy(tmp*m);
        k>>=1;
        m.Copy(m*m);
    }
    return tmp;
}
Matrix operate;
Matrix query[10005],change_x,change_y,ex,rot,moving;
void init()
{
    operate.m=3;operate.n=3;
    change_x.m=3;change_x.n=3;change_x.a[0][0]=1;change_x.a[1][1]=-1;change_x.a[2][2]=1;
    change_y.m=3;change_y.n=3;change_y.a[0][0]=-1;change_y.a[1][1]=1;change_y.a[2][2]=1;
    ex.m=3;ex.n=3;ex.a[2][2]=1;
    rot.m=3;rot.n=3;rot.a[2][2]=1;
    moving.m=3;moving.n=3;moving.a[0][0]=1;moving.a[1][1]=1;moving.a[2][2]=1;
}
int main()
{

    char k[3];
    double x,y;
    int n,m;
    init();
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
    {
        query[i].n=3;
        query[i].m=1;
        scanf("%lf%lf",&query[i].a[0][0],&query[i].a[1][0]);
        query[i].a[2][0]=1;
    }
    for(int i=0; i<3; i++)
        operate.a[i][i]=1;
    for(int i=0; i<m; i++)
    {
        scanf("%s",k);
        if(k[0]=='X')
            operate.Copy(change_x*operate);
        if(k[0]=='Y')
            operate.Copy(change_y*operate);
        if(k[0]=='M')
        {
            scanf("%lf%lf",&x,&y);
            moving.a[0][2]=x;
            moving.a[1][2]=y;
            operate.Copy(moving*operate);
        }
        if(k[0]=='S')
        {
            scanf("%lf",&x);
            ex.a[0][0]=x;
            ex.a[1][1]=x;
            operate.Copy(ex*operate);
        }
        if(k[0]=='R')
        {
            scanf("%lf",&x);
            x=x/180.0*PI;
            rot.a[0][0]=cos(x);
            rot.a[0][1]=-sin(x);
            rot.a[1][0]=sin(x);
            rot.a[1][1]=cos(x);
            operate.Copy(rot*operate);
        }
    }
    for(int i=0; i<n; i++)
    {
        query[i].Copy(operate*query[i]);
        printf("%.1lf %.1lf\n",query[i].a[0][0],query[i].a[1][0]);
    }
}

类型二:矩阵快速幂+矩阵的迹

题目:Tr A

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1575

题意:求矩阵A^k的迹的对9973的模。

思路:基本裸题的矩阵快速幂。题目的意义在于回忆起矩阵的迹的定义。

代码:(乘法取模版)

#define MOD 9973
#define maxn 15
#define maxm 15
using namespace std;
struct Matrix
{
    int n,m;
    int a[maxn][maxm];
    void init()
    {
        n=m=0;
        memset(a,0,sizeof(a));
    }
    Matrix operator +(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]+b.a[i][j];
        return tmp;
    }
    Matrix operator -(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]-b.a[i][j];
        return tmp;
    }
    Matrix operator *(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<b.m; j++)
                tmp.a[i][j]=0;
        }
        for(int i=0; i<n; i++)
            for(int j=0; j<b.m; j++)
                for(int k=0; k<m; k++)
                {
                    tmp.a[i][j]+=a[i][k]*b.a[k][j];
                }
        for(int i=0; i<n; i++)
            for(int j=0; j<b.m; j++)
                tmp.a[i][j]%=MOD;
        return tmp;
    }
    void Copy(const Matrix &x)
    {
        n=x.n;
        m=x.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                a[i][j]=x.a[i][j];
    }
};
Matrix M_quick_pow(Matrix m,int k)
{
    Matrix tmp;
    tmp.n=m.n;
    tmp.m=m.m;
    for(int i=0; i<tmp.n; i++)
    {
        for(int j=0; j<tmp.n; j++)
        {
            if(i==j)
                tmp.a[i][j]=1;
            else tmp.a[i][j]=0;
        }
    }
    while(k)
    {
        if(k&1)
            tmp.Copy(tmp*m);
        k>>=1;
        m.Copy(m*m);
    }
    return tmp;
}
int main()
{
    int T;
    scanf("%d",&T);
    Matrix A;
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        A.m=n;
        A.n=n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&A.a[i][j]);
        A=M_quick_pow(A,k);
        int ans=0;
        for(int i=0;i<n;i++)
            ans=(ans+A.a[i][i])%MOD;
        printf("%d\n",ans);
    }
}

类型三:二分+矩阵快速幂

题目一:Gauss Fibonacci

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1588

题意:给出k,b,n,M(k,b,n,M<=1e9),问对于所有i(0<=i<n),第g(i)=i*k+b个斐波那契数的和是多少。

思路:第一反应不是反应是直接求,看到数据范围以后就明白了。和之前做过的一道题差不多,都是矩阵快速幂加二分。

举个例子就是A^1+A^2+A^3+A^4+...+A^9+A^10=(1+A^5)(A^1+A^2+...+A^5),如此二分,注意奇偶即可。

代码:

LL kk,bb,nn,M;
Matrix A,B,C,ans,first,aa,m;
void init()
{
    A.init();B.init();C.init();ans.init();first.init();aa.init();m.init();
    ans.m=2;ans.n=2;
    first.n=2;first.m=1;first.a[0][0]=0;first.a[1][0]=1;
    A.m=2;A.n=2;A.a[0][1]=1;A.a[1][0]=1;A.a[1][1]=1;
    B.m=2;B.n=2;B.a[0][0]=1;B.a[1][1]=1;
    nn--;
    aa.Copy(M_quick_pow(A,bb));
    C.Copy(M_quick_pow(A,kk));
}
int main()
{
    while(~scanf("%I64d%I64d%I64d%I64d",&kk,&bb,&nn,&M))
    {
        init();
        m.Copy(B);
        while(nn)
        {
            if(nn==1)
                B.Copy(B*C);
            else
            {
                if(nn%2==1)
                    ans.Copy(ans+B*M_quick_pow(C,nn));
                B.Copy(B*(m+M_quick_pow(C,nn/2)));
            }
            nn>>=1;
        }
        ans.Copy((ans+m+B)*aa);
        first.Copy(ans*first);
        printf("%I64d\n",first.a[0][0]);
    }
    return 0;
}

题目二:Matrix Power Series

解题报告:http://blog.csdn.net/ooooooooe/article/details/38413515

 

类型四:数组位置的交换

题目:送给圣诞夜的礼品

链接:https://vijos.org/p/1049

题意:给出n,m,k(1<=n<=100;1<=m<=10;1<=k<=2^31-1),一个数组的长度为n,每次操作给出一个长度为n的数组,数组第i位表示把原数组第a[i]个数移到第i位,一共有m种操作,一共操作k次,1~m操作结束后循环操作,直到k次操作全部完成,输出每个位置的数。

思路:由于k极大,所以一次操作的话一定会超时,所以用矩阵快速幂,记录一次完成操作之后的位置交换方式,进行k/m次操作,然后进行k%m次从第一种操作开始的操作。

需要注意的是如果把最初数组放在n*1的矩阵当中,那么每次操作都要进行左乘,如果是放在1*n的矩阵当中,那么每次操作都要进行右乘。

代码:

#define maxn 105
#define maxm 105
int main()
{
    int n,m,k,a,b;
    scanf("%d%d%d",&n,&m,&k);
    Matrix A,all_cal,each_cal,last_cal;
    A.init();
    all_cal.init();
    last_cal.init();
    all_cal.n=n;
    all_cal.m=n;
    last_cal.n=n;
    last_cal.m=n;
    A.n=n;
    A.m=1;
    for(int i=0; i<n; i++)
    {
        A.a[i][0]=i+1;
        all_cal.a[i][i]=1;
        last_cal.a[i][i]=1;
    }
    a=k/m;
    b=k%m;
    for(int i=1; i<=m; i++)
    {
        each_cal.init();
        each_cal.m=n;
        each_cal.n=n;
        for(int j=0; j<n; j++)
        {
            int x;
            scanf("%d",&x);
            each_cal.a[j][x-1]=1;
        }
        all_cal=each_cal*all_cal;
        if(i==b)
            last_cal.Copy(all_cal);
    }
    all_cal.Copy(M_quick_pow(all_cal,a));
    all_cal=last_cal*all_cal;
    A.Copy(all_cal*A);
    for(int i=0; i<n; i++)
    {
        printf("%d",A.a[i][0]);
        if(i<n-1)
            printf(" ");
    }
    printf("\n");
    return 0;
}

类型五:数组的递推

题目:Fibonacci

链接:http://poj.org/problem?id=3070

题意:求第n个斐波那契数(0 ≤ n ≤ 1000000000)。

思路:对于这种在O(n)复杂度不能推出的数,果断想到快速幂,不过题里说得实在是太清楚了,连递推的矩阵都给出来了。

代码:

int main()
{
    int tot;
    while(scanf("%d",&tot)&&~tot)
    {
        Matrix a;
        a.m=a.n=2;
        a.a[0][0]=a.a[0][1]=a.a[1][0]=1;
        a.a[1][1]=0;
        if(tot==0)
            printf("0\n");
        else
        {
            Matrix ans=M_quick_pow(a,tot);
            printf("%d\n",ans.a[0][1]);
        }
    }
    return 0;
}

类型六:DP加速

题目:Warcraft III 守望者的烦恼

链接:https://vijos.org/p/1067

题意:给出k,n(1<=k<=10,1<=n<=2^31-1).每一步可以走1~k的距离,求走到n距离有多少种走法。

思路:如果只是单纯的DP的话,复杂度是O(N^2),公式是dp[i]=dp[i-1]+dp[i-2]+...+dp[i-k]。可以用矩阵快速幂来加速。

不断用矩阵左乘,就可以用快速幂来加速了。

代码:

#define maxn 15
#define maxm 15
#define MOD 7777777
struct Matrix
{
    int n,m;//n是列数,m是行数
    LL a[maxn][maxm];
    void init()
    {
        n=m=0;
        memset(a,0,sizeof(a));
    }
    void change(int c)
    {
        n=c;
        m=c;
        memset(a,0,sizeof(a));
        for(int i=0;i<c;i++)
            a[0][i]=1LL;
        for(int i=1;i<c;i++)
            a[i][i-1]=1LL;
    }
    Matrix operator +(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]+b.a[i][j];
        return tmp;
    }
    Matrix operator -(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]-b.a[i][j];
        return tmp;
    }
    Matrix operator *(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<b.m; j++)
                tmp.a[i][j]=0;
        }
        for(int i=0; i<n; i++)
            for(int j=0; j<b.m; j++)
                for(int k=0; k<m; k++)
                {
                    tmp.a[i][j]+=a[i][k]*b.a[k][j];
                    tmp.a[i][j]=(tmp.a[i][j]%MOD+MOD)%MOD;
                };
        return tmp;
    }
    void Copy(const Matrix &x)
    {
        n=x.n;
        m=x.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                a[i][j]=x.a[i][j];
    }
};//只有当矩阵A的列数与矩阵B的行数相等时A×B才有意义
Matrix M_quick_pow(Matrix m,int k)
{
    Matrix tmp;
    tmp.n=m.n;
    tmp.m=m.m;//m=n才能做快速幂
    for(int i=0; i<tmp.n; i++)
    {
        for(int j=0; j<tmp.n; j++)
        {
            if(i==j)
                tmp.a[i][j]=1;
            else tmp.a[i][j]=0;
        }
    }
    while(k)
    {
        if(k&1)
            tmp.Copy(tmp*m);
        k>>=1;
        m.Copy(m*m);
    }
    return tmp;
}
int main()
{
    Matrix A,ans;
    int k,n;
    scanf("%d%d",&k,&n);
    A.change(k);
    ans.init();
    ans.n=k;
    ans.m=1;
    ans.a[0][0]=1;
    A.Copy(M_quick_pow(A,n));
    ans.Copy(A*ans);
    printf("%I64d\n",ans.a[0][0]);
    return 0;
}

种类七:路径种类数

题目一:How many ways??

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2157

题意:给出n个节点m条单向边(0 < n <= 20, m <= 100),问从s点到t点经过k个其他节点有多少种走法。

思路:用临接矩阵A记录路径,记录的路径也表示从i到j是否能一次到达。B=A*A表示的是i到j能两次到达的走法。B[i][j]=ΣA[i][k]*A[k][j]。

P.S.好像很早刚学临接矩阵的时候就想过这种用法,到这实现了。

代码:

int main()
{
    Matrix A,ans[25];
    int a,b,c,x,y,z;
    while(scanf("%d%d",&a,&b))
    {
        if(a+b==0)
            break;
        A.init();
        A.m=a;
        A.n=a;
        for(int i=0; i<b; i++)
        {
            scanf("%d%d",&x,&y);
            A.a[x][y]=1;
        }
        for(int i=0; i<20; i++)
            ans[i].init();
        ans[0].m=a;
        ans[0].n=a;
        for(int i=0;i<a;i++)
            ans[0].a[i][i]=1;
        ans[1].Copy(A);
        ans[1].m=a;
        ans[1].n=a;
        for(int i=2; i<20; i++)
            ans[i].Copy(ans[i-1]*A);
        scanf("%d",&c);
        for(int i=0; i<c; i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            printf("%d\n",ans[z].a[x][y]);
        }
    }
    return 0;
}

类型七点五:路径种类数变种

题目:Cow Relays

链接: http://poj.org/problem?id=3613

题意:给出起点S和终点E,求从S到E的N步最短路(经过N个节点)。

思路:弗洛伊德算法的“慢动作”,A矩阵的a[i][j]代表经过a个节点的最短路,B矩阵的a[i][j]代表经过b个节点的最短路,那么C矩阵的a[i][j]的弗洛伊德结果就是经过了a+b个节点的最短路,如果经过a+b个节点不能到达,那么就是INF。因为N极大,所以递推O(N)也会超时,用到了矩阵快速幂。

P.S.我开的布尔数组默认值不是false,需要我自己重置一下才可以,WA一发。

代码:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x3fffffff
#define PI acos(-1.0)
#define seed 31//131,1313
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
#define maxn 205
#define maxm 205
struct Matrix
{
    int n,m;//n是列数,m是行数
    int a[maxn][maxm];
    void init()
    {
        n=m=0;
        for(int i=0;i<=200;i++)
            for(int j=0;j<=200;j++)
                a[i][j]=INF;
    }
    void change(int c,int d)
    {
        n=c;
        m=d;
    }
    Matrix operator +(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]+b.a[i][j];
        return tmp;
    }
    Matrix operator -(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                tmp.a[i][j]=a[i][j]-b.a[i][j];
        return tmp;
    }
    Matrix operator *(const Matrix &b) const
    {
        Matrix tmp;
        tmp.n=n;
        tmp.m=b.m;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<b.m; j++)
                tmp.a[i][j]=INF;
        }
        for(int i=0; i<n; i++)
            for(int j=0; j<b.m; j++)
                for(int k=0; k<m; k++)
                    tmp.a[i][j]=min(tmp.a[i][j],a[i][k]+b.a[k][j]);
        return tmp;
    }
    void Copy(const Matrix &x)
    {
        n=x.n;
        m=x.m;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                a[i][j]=x.a[i][j];
    }
};
Matrix M_quick_pow(Matrix m,int k)
{
    Matrix tmp;
    tmp.init();
    tmp.n=m.n;
    tmp.m=m.m;
    for(int i=0;i<tmp.n;i++)
        tmp.a[i][i]=0;
    while(k)
    {
        if(k&1)
            tmp.Copy(tmp*m);
        k>>=1;
        m.Copy(m*m);
    }
    return tmp;
}
int main()
{
    int N,T,S,E,top=0,x,y,z,M[maxn*10];
    bool vis[maxn*10];
    memset(vis,0,sizeof(vis));
    Matrix A;
    A.init();
    scanf("%d%d%d%d",&N,&T,&S,&E);
    for(int i=0;i<T;i++)
    {
        scanf("%d%d%d",&z,&x,&y);
        if(!vis[x])
        {
            vis[x]=1;
            M[x]=top++;
        }
        if(!vis[y])
        {
            vis[y]=1;
            M[y]=top++;
        }
        if(z<A.a[M[x]][M[y]])
        {
            A.a[M[x]][M[y]]=z;
            A.a[M[y]][M[x]]=z;
        }
    }
    A.m=A.n=top;
    A.Copy(M_quick_pow(A,N));
    printf("%d\n",A.a[M[S]][M[E]]);
    return 0;
}


  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值