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