zoukankan      html  css  js  c++  java
  • 矩阵十题(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
  • 相关阅读:
    51nod 1087 1 10 100 1000(找规律+递推+stl)
    51nod 1082 与7无关的数 (打表预处理)
    51 nod 1080 两个数的平方和
    1015 水仙花数(水题)
    51 nod 1003 阶乘后面0的数量
    51nod 1002 数塔取数问题
    51 nod 1001 数组中和等于K的数对
    51 nod 1081 子段求和
    51nod 1134 最长递增子序列 (O(nlogn)算法)
    51nod 1174 区间中最大的数(RMQ)
  • 原文地址:https://www.cnblogs.com/frog112111/p/3089802.html
Copyright © 2011-2022 走看看