• 矩阵十题(8)


    经典题目8

    hdu  2157  How many ways??

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

    题目大意:给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值
    把 给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就 等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的 路径数,我们只需要二分求出A^k即可。

    代码如下:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define N 21
     4 #define M 1000
     5 struct Matrix
     6 {
     7     int edge[N][N];
     8 }map,res,tmp,map2;
     9 int n,m;
    10 Matrix mul(Matrix x,Matrix y)  //矩阵连乘
    11 {
    12     int i,j,k;
    13     memset(tmp.edge,0,sizeof(tmp.edge));
    14     for(i=0;i<n;i++)
    15         for(j=0;j<n;j++)
    16             for(k=0;k<n;k++)
    17             {
    18                 tmp.edge[i][j]+=(x.edge[i][k]*y.edge[k][j])%M;
    19                 tmp.edge[i][j]%=M;
    20             }
    21     return tmp;
    22 }
    23 void quickpow(int k)   //二进制思想
    24 {
    25     int i;
    26     memset(res.edge,0,sizeof(res.edge));
    27     for(i=0;i<n;i++)   //初始化为单位矩阵
    28             res.edge[i][i]=1;
    29     while(k)
    30     {
    31         if(k&1)
    32             res=mul(res,map2);
    33         map2=mul(map2,map2);
    34         k>>=1;
    35     }
    36 }
    37 int main()
    38 {
    39     int i,a,b,k,j,s,e,t;
    40     while(scanf("%d%d",&n,&m)!=EOF)
    41     {
    42         if(n==0&&m==0)
    43             break;
    44         memset(map.edge,0,sizeof(map.edge));
    45         for(i=0;i<m;i++)
    46         {
    47             scanf("%d%d",&a,&b);
    48             map.edge[a][b]=1;
    49         }
    50         scanf("%d",&t);
    51         while(t--)
    52         {
    53             scanf("%d%d%d",&s,&e,&k);
    54             for(i=0;i<n;i++)
    55                 for(j=0;j<n;j++)
    56                     map2.edge[i][j]=map.edge[i][j];
    57             quickpow(k);
    58             printf("%d\n",res.edge[s][e]%M);
    59         }
    60     }
    61     return 0;
    62 }
    View Code

    poj  3613  Cow Relays

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

    题目大意:   给出一张无向连通图,求S到E经过k条边的最短路。

    解题思路:    利用递推的思路,先算出经过一条边的最短路,再算两条边......k-1条边,k条边的最短路

                      先看一下Floyd的核心思想: edge[i][j]=min(edge[i][j],edge[i][k]+edge[k][j])

                      i到j的最短路是i到j的直接路径或者经过k点的间接路径,但是矩阵的更新总是受到上一次更新的影响

                      如果每次的更新都存进新矩阵,那么edge[i][k]+edge[k][j]是不是表示只经过三个点两条边的路径呢?

                      min(edge[i][j],edge[i][k]+edge[k][j])就表示只经过三个点两条边的最短路

                      方程:F[i][j]m=min(F[i][k]m-1+G[k][j]) {1kn,   m>1}

                      经过k条边的最短路,那么我们只需要把这个代码重复运行k次

    while(k)  
    {  
       res=mul(map,ant);  
       k--;  
    }

    这样显然答案是正确的,但是时间复杂度太高O(k*n^3),利用二进制的思想可以把时间复杂度降到O(logK*n^3)

    另外,存储边的邻接矩阵map.edge[i][i]不能初始化为0,为0时每次Floyd都会考虑走i--->i这条边,实际上这条边是不存在的

    代码如下:

     1 //k步最短路
     2  #include<stdio.h>
     3  #include<string.h>
     4  #define INF 0x3f3f3f3f
     5  #define N 101
     6  struct Matrix
     7  {
     8      int edge[N][N];
     9  }map,tmp,res;
    10  int n,f[N*10];
    11  Matrix mul(Matrix x,Matrix y)    //floyd
    12  {
    13      memset(tmp.edge,INF,sizeof(tmp.edge));
    14      int i,j,k;
    15      for(i=1;i<=n;i++)
    16          for(j=1;j<=n;j++)
    17              for(k=1;k<=n;k++)
    18                  if(tmp.edge[i][j]>x.edge[i][k]+y.edge[k][j])
    19                      tmp.edge[i][j]=x.edge[i][k]+y.edge[k][j];
    20      return tmp;
    21  }
    22  void quickpow(int k)
    23  {
    24      int i;
    25      memset(res.edge,INF,sizeof(res.edge));
    26      for(i=1;i<=n;i++)
    27          res.edge[i][i]=0;
    28      while(k)    //二进制思想
    29      {
    30          if(k&1)
    31              res=mul(res,map);
    32          map=mul(map,map);
    33          k>>=1;
    34      }
    35  }
    36  int main()
    37  {
    38      int i,k,t,s,e,len,u,v;
    39      scanf("%d%d%d%d",&k,&t,&s,&e);
    40      memset(map.edge,INF,sizeof(map.edge));
    41      memset(f,-1,sizeof(f));
    42      int num=0;
    43      for(i=0;i<t;i++)
    44      {
    45          scanf("%d%d%d",&len,&u,&v);
    46          if(f[u]==-1)    //离散化
    47              f[u]=++num;
    48          if(f[v]==-1)    //离散化
    49              f[v]=++num;
    50          map.edge[f[u]][f[v]]=map.edge[f[v]][f[u]]=len;
    51      }
    52      n=num;
    53      quickpow(k);  
    54      printf("%d\n",res.edge[f[s]][f[e]]);
    55      return 0;
    56  }
    View Code
  • 相关阅读:
    终于以一个ACMer的名义开通博客了。。
    Struts学习笔记一
    Matplotlib画图
    设计模式第一集——策略模式
    Hibernate学习笔记
    在linux下加python path【转】
    linux学习笔记
    C#3.0初体验
    asp.net中使用ffmpeg
    常用的正则表达式(经典)
  • 原文地址:https://www.cnblogs.com/frog112111/p/3089802.html
Copyright © 2020-2023  润新知