最短路优化

最短路优化

写在前面

上次讲了最短路的基础,但是像最短路这种博大精深(坑特别深)的算法。。。是肯定有优化的啦。这一篇是给有最短路基础的人看的,假如没有嘛。。可以看看我以前写的最短路基础

SPFA

我把这个算法挪到这边来写了,原因有两个:第一个是我上次懒得写了。。
第二个是这个算法比较难,所以放在了优化这边一起写,而且它本身就是对贝尔曼福德的优化。
不说废话了,let's begin
spfa 算法可以适用于负边权的情况,是 bellman-ford 的队列优化。
假设存在 G=<V,E>,dis[i]记录 V0 到 i 的最短距离,pre[i]记录从 V0 到 i 路径 上 i 的前面的一个顶点;
step1.将所有顶点对之间距离初始化为无穷大(dis[i][j]=无穷大),pre[i]=i, vis[i]=0,将源点入队;
step2.读取队头顶点 now,并将队头顶点 now 出队(记得消除标记),将与点 now 相连的所有点 next 进行松弛操作(还记得吗,上一篇讲过),更新 dis[next],另外,如果点 next 没有 在队列中,那么要将点 next 入队(记得标记),如果已经在队列中了,那么就不 用入队(如果某个顶点入队超过 V 次,则说明图中有负环,直接跳出);
step3.重复 step2,直到队空为止就完成了单源最短路的求解。
这是主要思路。
下面有个图解(无耻的转自某博客)?
spfa_1.jpgspfa_2.jpg

其实它和bfs非常类似(bfs在我创建的专题里有人讲过)。只是bfs中的一个点出了队列就不会再回来了,而最段路中由于有环的存在,导致了一个点可能进入队列多次。
spfa函数附上
spfa函数.png

下面是洛谷题解的一个,感觉注释写的挺详细,也附上。

void spfa()
{
  queue<int> q; //spfa用队列,这里用了STL的标准队列
  for(int i=1; i<=n; i++) 
  {
    dis[i]=inf; //带权图初始化
    vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组
  }
  q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记
  while(!q.empty())
  {
    int u=q.front(); //取出队首
    q.pop(); vis[u]=0; //出队标记
    for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替)
    {
      int v=edge[i].to; 
      if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改
      {
        dis[v]=dis[u]+edge[i].dis;
        if(vis[v]==0) //未入队则入队
        {
          vis[v]=1; //标记入队
          q.push(v);
        }
      }
    }
  }
}

spfa这个算法非常好用(功能强大,效率高?)
基本上单源最短路的题都可以用spfa,墙裂推荐

狄杰斯特拉(堆优化)

真不知道为什么会有人把它念成迪克特斯拉?
(需要有堆的基础)
思路:在寻找周围最小点的时候,用最小堆,可以用 O(log n)的时间找出最小的可到达点。
是不是感觉很蛋疼。。但是很秀?
最小堆:最小堆,是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值。
最小堆后面可能会说,但是这里嘛。。就很不负责任的推荐一个别人的博客:别人家的最小堆
别嫌弃呀。。
堆优化的主要思想就是使用一个优先队列(就是每次弹出的元素一定是整个队列中最小的元素)来代替最近距离的查找,用邻接表代替邻接矩阵,这样可以大幅度节约时间。
在这里有几个坑要先填一下:
首先来讲,优先队列的数据类型应该是怎样的呢?
我们知道优先队列应该用于快速寻找距离最近的点。由于优先队列只是将最小的那个元素排在前面,因此我们应该定义一种数据类型,使得它包含该节点的编号以及该节点当前与起点的距离。
辣么。。我们应该在什么时候对队列进行操作呢?
队列操作的地方,首先就是搜索刚开始,要为起点赋初始值,此时必须将起点加入优先队列中。该队列元素的节点编号为起点的编号,该节点当前与起点的距离为0。
那么如果一个节点到起点的最短距离通过其他的运算流程发生了变化,那么如何处理队列中的那个已经存入的元素?
事实上,你不需要理会队列中的元素(暴力无视),而是再存入一个就行了。因为如果要发生变化,只能将节点与起点之间的距离变得更小,而优先队列恰好是先让最小的那个弹出。
因此,轮到某一个队列元素弹出的时候,如果有多个元素的节点编号相同,那么被弹出的一定是节点编号最小的一个。等到后面再遇到这个节点编号的时候,我们只需要将它忽略掉就行了。
是不是感觉hin棒??
最小堆优化的迪杰有一个模版题:

例:网吧
【问题背景】
绵阳中学以人数众多而闻名。三个年级共有 10000 多人,学生多了附近的 网吧也多。Mzoiers 都热衷于 Dota,可学校的机子配置相当差(评测服务器除外), 根本不能玩 Dota,那就只有去网吧。星期天到星期五都是晚上 10:20 才下晚自 习,几乎没时间玩。
然而星期六下午放假是绝好的时间,但是学校人多啊,一放学去网吧的人就 开始狂奔,竞争之激烈,抢到机子的难度非常之大。往往在我们到达网吧之前都 坐满了。
学校到网吧的路是错综复杂的,以致于到一个自己想去的网吧都有非常多的 路线可以选择,而路线的长度又不相同,这样就决定了要花费的时间,因此想要 尽快到达,选择一条最佳的线路是很有必要的。
【问题描述】
为了简化问题,我们把学校与周边的网吧看做图中的顶点,学校与网吧,网 吧与网吧之间的路线看做边,每个边都有一个权,表示我们走完这条路的时间, 由于放学人流量大,如果反向走会有危险,因此这是一个有向图。
我的的学校在 S 点,想要去的网吧在 T 点。你的任务就是选择一条最佳路线, 使得从学校到目的地网吧的时间最短,你只需要输出最短到达时间即可。 【输入文件】
netbar.in 中共有 M+2 行数据
第一行两个整数 N,M,表示点数和边数。
然后 M 行每行 3 个正整数(u,v,t),表示有一条可由 u 到 v 耗时为 t 的边。 最后一行两个正整数 S、T。
【输出文件】
netbar.out 中,只有一行,一个整数表示最短时间。如果 S、T 之间不存在通 路则输出“No Solution!”(双引号不输出,“!”为西文标点)。
【输入样例】
44 123 2 4 10 135 345 14
【输出样例】
10
【数据规模】
对于 30%的数据保证有 1<N<=1000,1<=M<=1000; 对于全部的数据保证有 1<N<=10000,1<=M<=100000。

注明:我不是绵中的(也不打算去),我和绵中也没有什么利益关系。题目不是我出的,如果觉得它对绵中不怀好意。。。也别找我
下面附上标程:
迪杰1.jpg
迪杰2.jpg

迪杰3.png

迪杰 4.jpg

迪杰优化一般情况下用的不是很多,但是在某些特殊场合是可以用的。也看得出来优化不是很复杂,比较好理解。对迪杰情有独钟的同学可以记一下。

ending

最短路这个博大精深的算法也算是图论的核心,所以。。学好最短路很重要。
看在我这么用心的份上滋瓷一下呗。。
不要问我为什么这篇的程序都是图片。。
其实。。介绍一个方法(也不知道算不算了)。在遇到最段路的题的时候,windows系统的同学可以打开画图,把样例画成一个图,在推一遍,可以看得很直观,免得对着几个数字空想,很抽象。
(老师附体)(苦口婆心)以上内容可能有点难度,建议多看几遍,多想几下,最好能推一遍。。。
(在你嫌弃我之前跑掉)?

转载于:https://www.cnblogs.com/opbnbjs/p/9385684.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值