zoukankan      html  css  js  c++  java
  • 矩阵乘法

    p1353:

    一个由自然数组成的数列按下式定义:
    对于i <= k:ai = bi 
    对于i > k: ai = c[1]*a[i-1] + c[2]*a[i-2] + ... + c[k]*a[i-k]
    其中bj 和 cj (1<=j<=k)是给定的自然数。写一个程序,给定自然数m <= n,
    计算a[m] + a[m+1] + a[m+2] + ... + a[n], 并输出它除以给定自然数p的余数的值

    矩阵乘法应用入门题,设矩阵A={a1,a2,a3...an-1,an,Sn},然后再构造一个B矩阵,使A*B=A’,A’={a2,a3,a4...an,an+1,Sn+1};用快速幂优化,求Sn,Sm-1,答案就出来了;

    下面代码,因为是入门,所以也就没写什么struct Matrix;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<cstdlib>
     6 #include<ctime>
     7 #include<vector>
     8 #include<algorithm>
     9 #include<queue>
    10 using namespace std;
    11 #define LL long long
    12 const int maxn=20;
    13 LL k,mod,m,n;
    14 int a[maxn],b[maxn][maxn];
    15 int y[maxn][maxn],c[maxn];
    16 LL find(LL q){
    17     if(q<=0)return 0;
    18     memcpy(y,b,sizeof(y));
    19     int ans[maxn][maxn],t[maxn][maxn];
    20     memset(ans,0,sizeof(ans));
    21     memset(t,0,sizeof(t));
    22     for(int i=1;i<=k;i++)ans[i][i]=1;
    23     while(q!=0){
    24         if(q%2==1){
    25             memset(t,0,sizeof(t));
    26             for(int i=1;i<=k;i++)
    27                 for(int j=1;j<=k;j++)
    28                     for(int l=1;l<=k;l++)
    29                         t[i][j]=((LL)t[i][j]+((LL)ans[i][l]*y[l][j])%mod)%mod;
    30             memcpy(ans,t,sizeof(t));
    31         }
    32         q/=2;
    33         memset(t,0,sizeof(t));
    34         for(int i=1;i<=k;i++)
    35             for(int j=1;j<=k;j++)
    36                 for(int l=1;l<=k;l++)
    37                     t[i][j]=(t[i][j]+((LL)y[i][l]*y[l][j])%mod)%mod;
    38         memcpy(y,t,sizeof(t));
    39     }
    40     LL sum=0;
    41     for(int i=1;i<=k;i++)sum=(sum+(LL)a[i]*ans[k][i]%mod)%mod;
    42     return sum;
    43 }
    44 int main(){
    45     cin>>k;
    46     for(int i=1;i<=k;i++)cin>>a[i];
    47     for(int i=1;i<k;i++)b[i][i+1]=1;
    48     for(int i=1;i<=k;i++)cin>>b[k][k-i+1];
    49     b[k+1][1]=1;
    50     b[k+1][k+1]=1;
    51     k++;
    52     cin>>m>>n>>mod;
    53     printf("%d
    ",(find(n)-find(m-1)+mod)%mod);
    54     return 0;
    55 }
    View Code

    p1354:

    HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间
    内,走过一定的距离。
    但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。
    又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多
    少种散步的方法。
    现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地
    点A走到给定地点B共有多少条符合条件的路径;

    这道题的原型是二维动归入门,然后这道题经历了以下变迁: 二维dp入门->二维dp(图论形式)->二维dp(矩阵乘法优化)->二维dp(矩阵乘法优化)+点边转化;

    二维dp入门:f[t][i][j]=f[t-1][i-1][j]+f[t-1][i][j-1]+f[t-1][i+1][j]+f[t-1][i][j-1];很水;

    二维dp(图论形式):建成图,然后f[t][i]=sum(f[t-1][j]+d[j][i]);

    二维dp(矩阵乘法优化):t很大的时候,上矩阵乘法,建一个邻接矩阵,利用矩阵乘法转移;

    这道题:由于不能走上一步走的路,点边转化一下,拆成两条有向边,其他和上面一样;

    写得很难受,从来没在图上搞这么难受的设计,尤其是在不知道有没有效果的情况下;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<cstdlib>
     6 #include<ctime>
     7 #include<vector>
     8 #include<algorithm>
     9 #include<queue>
    10 using namespace std;
    11 #define LL long long
    12 const int maxn=125;
    13 const int mod=45989;
    14 struct node{
    15     int x,y,next;
    16 }e[maxn];
    17 int linkk[maxn],len=-1;
    18 int n,m,t,A,B;
    19 struct Matrix{
    20     int v[maxn][maxn];
    21     Matrix(){memset(v,0,sizeof(v));}
    22     friend Matrix operator*(const Matrix& a,const Matrix& b){
    23         Matrix c;
    24         for(int i=0;i<=len;i++)
    25             for(int j=0;j<=len;j++)
    26                 for(int k=0;k<=len;k++)
    27                     c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j]%mod)%mod;
    28         return c;
    29     }
    30     friend Matrix operator^(Matrix a,int b){
    31         Matrix c;
    32         for(int i=0;i<=len;i++)c.v[i][i]=1;
    33         while(b){
    34             if(b%2)c=c*a;
    35             b/=2;
    36             a=a*a;
    37         }
    38         return c;
    39     }
    40 }a,b;
    41 void insert(int x,int y){
    42     e[++len].y=y;e[len].next=linkk[x];linkk[x]=len;e[len].x=x;
    43 }
    44 void init(){
    45     memset(linkk,-1,sizeof(linkk));
    46     cin>>n>>m>>t>>A>>B;
    47     int x,y;A++,B++;
    48     for(int i=1;i<=m;i++){
    49         cin>>x>>y;
    50         x++,y++;
    51         insert(x,y);insert(y,x);
    52     }
    53     for(int i=linkk[A];i!=-1;i=e[i].next)a.v[1][i]++;
    54     for(int i=0;i<=len;i++)
    55         for(int j=0;j<=len;j++)
    56             if(e[i].y==e[j].x)if(i!=(j^1))b.v[i][j]++;
    57     a=a*(b^(t-1));
    58     LL sum=0;
    59     for(int i=linkk[B];i!=-1;i=e[i].next)sum=(sum+a.v[1][i^1])%mod;
    60     cout<<sum%mod<<endl;
    61 }
    62 int main(){
    63     init();
    64 }
    View Code

    p1355:

    FJ的N(2 <= N <= 1,000,000)头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点,自然是在牧场中现有的T(2 <= T <= 100)条跑道上。 

      农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点I1_i和I2_i(1 <= I1_i <= 1,000; 1 <= I2_i <= 1,000)。每个交汇点都是至少两条跑道的端点。奶牛们知道每条跑道的长度length_i(1 <= length_i <= 1,000),以及每条跑道连结的交汇点的编号。并且,没有哪两个交汇点由两条不同的跑道直
    接相连。你可以认为这些交汇点和跑道构成了一张图。

      为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。

      你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道.

     

    这道题和上道题的转移方程差不多:f[t][i]=min(f[t-1][j]+d[j][i]);

    但不同的是上一题是sum,这一题是min。

    和上题一样,建立矩阵,但矩阵的乘法规则换成c[i][j]=min(a[i][k]+b[k][j]);

    然后就可以转移了,初始矩阵^n表示走n步后的最短长度;

    在看题解之前还真不知道矩阵乘法的规则可以这么改变;

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<ctime>
    #include<vector>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define LL long long
    const int maxn=1010;
    const int inf=1000000000;
    struct node1{
        int x,y,v,next;
    }e[maxn];
    int linkk[maxn],len=0,n,N,m,S,E,top=0;
    struct node{int x,y,v;}b[maxn];
    bool flag[maxn];
    int q[maxn],f[maxn];
    void insert(int x,int y,int v){
        e[++len].y=y;e[len].x=x;
        e[len].next=linkk[x];
        linkk[x]=len;
        e[len].v=v;
    }
    void init(){
        scanf("%d%d%d%d",&n,&m,&S,&E);
        int x,y,v;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&v,&x,&y);b[i].x=x,b[i].y=y,b[i].v=v;
            if(!flag[x])q[++top]=x,flag[x]=1;
            if(!flag[y])q[++top]=y,flag[y]=1;
        }
        sort(q+1,q+top+1);
        for(int i=1;i<=maxn;i++)f[q[i]]=i;
        for(int i=1;i<=m;i++){
            insert(f[b[i].x],f[b[i].y],b[i].v);
            insert(f[b[i].y],f[b[i].x],b[i].v);
        }
    }
    struct Mat{
        LL v[202][202];
        Mat(){memset(v,0,sizeof(v));}
        Mat operator*(Mat& x){
            Mat c;
            memset(c.v,10,sizeof(c.v));
            for(int i=1;i<=top;i++)
                for(int j=1;j<=top;j++)
                    for(int k=1;k<=top;k++)
                    {
                        c.v[i][j]=min(c.v[i][j],v[i][k]+x.v[k][j]);
                    }
            return c;
        }
        Mat operator^(int b){
            Mat c;
            bool flag1=1;
            while(b){
                if(b%2)
                {
                    if(flag1)c=*this,flag1=0;
                    else c=c*(*this);
                }
                b/=2;
                *this=(*this)*(*this);
            }
            return c;
        }
    }a;
    void work(){
        memset(a.v,10,sizeof(a.v));
        for(int x=1;x<=top;x++){
            for(int i=linkk[x];i;i=e[i].next){
                a.v[x][e[i].y]=e[i].v;
            }
        }
        a=a^(n);
        cout<<a.v[f[S]][f[E]]<<endl;
    }
    int main(){
        init();
        work();
    }
    View Code

    矩阵乘法有这么一段话:如果一个向量是其他向量的线性组合,便可以考虑用矩阵乘法;

    这其实是在考验建模能力;

  • 相关阅读:
    WPF-触发器
    WPF使用socket实现简单聊天软件
    git常用命令备忘
    (转载)WPF中的动画——(一)基本概念
    WPF中的依赖项属性
    C#中的索引器
    C#中的装箱拆箱
    编程语言的弱类型、强类型、动态类型、静态类型
    WPF中的数据驱动
    WPF中的命令简介
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5868019.html
Copyright © 2011-2022 走看看