(代码部分待补)
核心:题意转化,建立抽象模型
注意数据范围选择对应模版:
稠密图点少边多(m≈n^2就可以认为是稠密),适合朴素dij或prim
稀疏图点多边少,适合堆优化dij
各个算法简单总结
1.Spfa O(n~mn)
循环队列写法:(也是实际应用中的模版)
因为点的反复入队,无法确定队列长度,容易爆内存
因此使用循环队列,改动如下:
①初始化时hh=0,tt=0;
②取队首弹出后if(hh==n) hh=0;
③定点入队后if(tt==n) tt=0;
2.Dijkstra(堆优化) O(mlogn)
类似bfs,两者区别是:
①bfs最先搜到的点一定是最近的,所以入队前判重,每一个状态只会被更新一次。
②堆优化版dij每次更新堆都要重新排序,即每一个点可能被访问和更新多次,只有在出队的时候才可以保证当前的dis[ver]是最小的,所以在出队判重。
补充复习:
对于A*算法来说出队的时候不用判重,终点第k次出队的时候那么求解的是起点到终点的第k短路。
3.Bellmanford O(mn)
只用来求边数限制的最短路
(因为每一条边都会考虑,spfa则不是)
注意需要备份,松弛操作:dist[x.b] = min(dist[x.b], backup[x.a]+x.w);
实战训练
1.信使
题意:求从起点送到所有的点这整个过程的最短时间
转化:单源最短路(最短)的最大值(整个过程)
黄油
题意:选一个点,满足所有的点到他的距离之和最小
转化:求所有点到其他全部点的最短路
花费
题意:a给b转账,要想让b得到100钱,a最少准备多少钱
转化:(n~1e3,m~1e5)
设刚开始的钱是x,每两个人之间的交易率wi,则100=x∗(w1*w2*..)
x最小→w1∗w2∗w3..w1∗w2∗w3..最大
在这道题目中,我们可以用朴素dij(邻接矩阵)求最短路,数学证明过程:
我们以前的最短路求的都是和的最小值,那么就要:
①积化和——取对数
→log(w1)+log(w2)+...最大
②求最大转求最小
因为w<=1,所以log(w)<=0
那么将每个数取相反数,保证没有负权路(否则没有单调的“传递性”),就是求最短路了
注意初始化起点的时候w为1,不是0,w不是普通求最短路的dist
补充:最短路问题什么时候可以求乘积?
①0<w<=1:dij ②w>0:spfa
乘车
题意:已知公交路线,求从起点到终点的最少换乘次数
转化:已知边权均为1的有向图,求最短路-1(换乘!不是乘车!),直接bfs即可
注意特判,如果无法到达要输出0
聘礼
题意:买东西有两种方式,付所有的钱/交一个东西+付部分钱。已知一个部落,并且等级差距过大的人之间不会进行交易,求买到最终物品的最少钱
转化:每个人为顶点,满足等级差距+拥有可以抵押的物品则连边,边权为付的钱
但是要是选第一种方式,就无法按照这种方式建边了。那么就凭空出现一个人,它有一个价值为0的万能物品可以抵押所有人的条件→虚拟源点和所有已知的点连边
实现代码时注意取值范围