• 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 一样满足分配律;而且矩阵乘几次,它每个点所表示的就变为了恰好经过 n 的长度(可以走重边)到达这个点(坐标)的方案总数。(本题就是最短距离)所以我们是可以将矩阵乘法变为我们所需要的形式。

    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
    ",ans.a[start][done]);
    43 }
    View Code
    越努力 越幸运
  • 相关阅读:
    ubuntu下android开发工作环境搭建
    ADB命令行控制界面开关
    chromium os系统编译与环境搭建
    完整代理的简单实现
    OC协议、代理的简单使用
    OC字典的使用
    OC数组的简单使用、NSArray
    OC中NSString的使用、字符串的使用
    OC内存管理、非ARC机制、MRR机制
    OC中重写set和get方法、懒加载
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11202571.html
Copyright © 2020-2023  润新知