poj 3613 Cow Relays

题目链接:点我
题目大意: 给出一张无向连通图,求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]) {1≤k≤n,   m>1}

              经过k条边的最短路,那么我们只需要把这个代码重复运行k次
while(k)  
{  
   res=mul(map,ant);  
   k--;  
}

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

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

代码如下:

//k步最短路
 #include<stdio.h>
 #include<string.h>
 #define INF 0x3f3f3f3f
 #define N 101
 struct Matrix
 {
     int edge[N][N];
 }map,tmp,res;
 int n,f[N*10];
 Matrix mul(Matrix x,Matrix y)    //floyd
 {
     memset(tmp.edge,INF,sizeof(tmp.edge));
     int i,j,k;
     for(i=1;i<=n;i++)
         for(j=1;j<=n;j++)
             for(k=1;k<=n;k++)
                 if(tmp.edge[i][j]>x.edge[i][k]+y.edge[k][j])
                     tmp.edge[i][j]=x.edge[i][k]+y.edge[k][j];
     return tmp;
 }
 void quickpow(int k)
 {
     int i;
     memset(res.edge,INF,sizeof(res.edge));
     for(i=1;i<=n;i++)
         res.edge[i][i]=0;
     while(k)    //二进制思想
     {
         if(k&1)
             res=mul(res,map);
         map=mul(map,map);
         k>>=1;
     }
 }
 int main()
 {
     int i,k,t,s,e,len,u,v;
     scanf("%d%d%d%d",&k,&t,&s,&e);
     memset(map.edge,INF,sizeof(map.edge));
     memset(f,-1,sizeof(f));
     int num=0;
     for(i=0;i<t;i++)
     {
         scanf("%d%d%d",&len,&u,&v);
         if(f[u]==-1)    //离散化
             f[u]=++num;
         if(f[v]==-1)    //离散化
             f[v]=++num;
         map.edge[f[u]][f[v]]=map.edge[f[v]][f[u]]=len;
     }
     n=num;
     quickpow(k);  
     printf("%d\n",res.edge[f[s]][f[e]]);
     return 0;
 }

猜你喜欢

转载自blog.csdn.net/qq_37774171/article/details/81907818