其他分享
首页 > 其他分享> > relays 奶牛接力跑(矩阵快速幂求最短路径)

relays 奶牛接力跑(矩阵快速幂求最短路径)

作者:互联网

题干:

  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条跑道。

题解:

  看到 n 这么大吓了一跳,最后看到 n 其实就是路径长度,基本上这就是矩阵快速幂了。但是节点大小为1~1000,显然难以承受,但是它的边不过 100 条,所以它有用的点最多为 101 个(连通),通过离散化可以实现(或者先建边,dfs一遍标个号即可)。将路径长度作为指数,直接将每个边的长度作为矩阵的值进行矩阵快速幂。

  为什么要将将每个边的长度直接作为矩阵的值?不用缩点吗?若我们进行缩点,100000 个点一定无法接受。再者,其实若是求必经 n 条路的最短路径长度,我们矩阵的定义就变为了由 x 到 y 节点必须经过 z 条路径的最小值,而每个节点的值就是路径长度,所以不需缩点。

  但是如果是普通的矩阵快速幂,它求出的只是经过一个点的路径种类数,无法满足我们的预期,我们可以将矩阵乘法由题意改变一下,变为:

sum.a[i][j]=min(sum.a[i][j],aa.a[i][k]+bb.a[k][j]);

  可以发现,把矩阵乘法的∑改成取 min 一样满足分配律,所以我们是可以将矩阵乘法变为我们所需要的形式。

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define $ 111
 5 using namespace std;
 6 int m,n,t,start,done,in[$],sta[$*2],out[$],w[$],up;
 7 inline int min(int x,int y){    return x<y?x:y;    }
 8 struct tree{
 9     int a[$][$];
10     tree(){    memset(a,63,sizeof(a));    }
11     friend tree operator * (tree aa,tree bb){
12         tree sum=tree();
13         for(register int k=1;k<=m;++k)
14             for(register int i=1;i<=m;++i)
15                 for(register int j=1;j<=m;++j)
16                     sum.a[i][j]=min(sum.a[i][j],aa.a[i][k]+bb.a[k][j]);
17         return sum;
18     }
19     friend tree operator ^ (tree aa,int x){
20         tree sum=tree();
21         for(register int i=1;i<=m;++i) sum.a[i][i]=0;
22         for(;x;x>>=1,aa=aa*aa) if(x&1) sum=sum*aa;
23         return sum;
24     }
25 };
26 signed main(){
27     tree ans=tree();
28     scanf("%d%d%d%d",&n,&t,&start,&done);
29     for(register int i=1;i<=t;++i)
30         scanf("%d%d%d",&w[i],&in[i],&out[i]),sta[++up]=in[i],sta[++up]=out[i];
31     sta[++up]=start, sta[++up]=done;
32     sort(sta+1,sta+up+1);
33     m=unique(sta+1,sta+up+1)-sta-1;
34     for(register int i=1;i<=t;++i){
35         in[i]=lower_bound(sta+1,sta+m+1,in[i])-sta;
36         out[i]=lower_bound(sta+1,sta+m+1,out[i])-sta;
37         ans.a[in[i]][out[i]]=ans.a[out[i]][in[i]]=w[i];
38     }
39     start=lower_bound(sta+1,sta+m+1,start)-sta;
40     done= lower_bound(sta+1,sta+m+1,done)-sta;
41     ans=ans^n;
42     printf("%d\n",ans.a[start][done]);
43 }
View Code

 

标签:aa,幂求,int,sum,路径,矩阵,接力跑,长度,relays
来源: https://www.cnblogs.com/OI-zzyy/p/11202571.html