简单单源最短路径笔记QwQ
推荐阅读:
国家集训队论文2006 《最短路算法及其应用》 By 广东北江中学 余远铭
国家集训队论文2009 《迭代求解的神器——spfa算法的优化和应用》By 中山纪念中学 姜碧野
以及 《算法导论》
注意:以下模板题目是:P3371 luogu
luoGu的模板题比较坑,因为相邻的两点间的边可能有3条以上,所以对于floyed和dijkstra从i到j的边如果有多条要取最短的那条,
但是对于spfa和bellman-ford就不用啦 2333
Floyed ——O(n^3)
通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。——from 知乎
关键是枚举中间点
伪代码:
for k:= 1 to n do for i:= 1 to n do for j:= 1 to n do f[i,j]:=min(f[i,j],f[i,k]+f[k,j]);
巧妙的Floyd优化,删点那一题目——小X的最短路,从删光点开始往前推,每次增加一点,不能照搬Floyd,要分别以他为起点,终点,中转点来一次Floyd,复杂度大概O(N^3)
还能用来判断点的连通性。
Dijkstra ——O(n^2)
不能处理负边权!
图片来自维基百科
主要思想:
初始化数组为maxlongint div 3; c[i]表示起点到i点的最短距离
从起点开始找,直到找了n-1个点
每次找离当前点最近的蓝点【包括自己】,把它变白,以此为中转点更新与之相连的蓝点到起点的距离。
使它到起点的距离不再是maxlongint div 3或者其他,所以下一个找的蓝点说不定就是它,如图点7
图片来自《算法导论》
模板:
//纯dijkstra , Score: 70 MLE program shortest; var n,i,a,b,m,j,s,e,min,k,tmp:longint; x,y:array[1..500000] of longint; che:array[1..1000] of boolean; c:array[1..1000] of longint; f:array[1..1000,1..1000] of longint; begin readln(n,m,s); fillchar(f,sizeof(f),$5f); for i:= 1 to m do begin readln(x[i],y[i],tmp);//x表示边的左端点,y表示边的右端点 if (f[x[i],y[i]]>0) and (f[x[i],y[i]]>tmp) then f[x[i],y[i]]:=tmp; end; for i:= 1 to n do c[i]:=f[s,i]; c[s]:=0; for i:= 1 to n-1 do begin min:=maxlongint; k:=0; for j:= 1 to n do if che[j]=false then if c[j]<min then begin min:=c[j]; k:=j; end; if k=0 then break; che[k]:=true; for j:= 1 to n do if c[j]>f[k,j]+c[k] then c[j]:=f[k,j]+c[k];//如果要记录前驱要在这里的relax添加pre的记录 end; for i:= 1 to n do if c[i]<>1600085855 then write(c[i],' ') else write('2147483647 ') end.
Bellman-Ford——O(NE)
无法处理负权回环
思想
每次都对M条边做一遍relax操作,必然会有至少一个蓝点turn white.
假设最坏情况(relax),每次只有一个蓝点变白,那么我们至少要n次*m次的relax操作!
模板
//Score: 70 , DATE8-10: TLE program shortest; var n,i,a,b,m,j,s,e:longint; x,y:array[1..500000] of longint; w:array[1..500000] of longint; c:array[1..10000] of longint; procedure printf(); begin for i:= 1 to n do if c[i]<>2139062143 then write(c[i],' ') else write('2147483647 '); end; procedure init();//初始化过程 begin fillchar(c,sizeof(c),$7f); //这样会得到一个接近maxlongint的数——2147483647 c[s]:=0; end; function bellman_ford:boolean; begin init; for i:= 1 to n do for j:= 1 to m do if c[y[j]]>c[x[j]]+w[j] then c[y[j]]:=c[x[j]]+w[j]; //relax,注意有无向 {for j:= 1 to m do //判断负权回路 if (c[y[j]]>c[x[j]]+w[j]) then exit(false);}//根据路径松弛性质,最短路径中的边的最短路径估计值在达到下限后不再会改变,除非出现负权回路 exit(true); //虽然该题不会有负权回路 end; begin readln(n,m,s); for i:= 1 to m do readln(x[i],y[i],w[i]);//x表示边的左端点,y表示边的右端点 if bellman_ford then printf else writeln('Impossible'); end.
SPFA:
其他
以下内容来自《算法导论》第三版
relax操作
对最短路径的估计值的收紧【更新】并更新前驱
《算法导论》第三版
《迭代求解的神器——spfa算法的优化和应用》
相关性质
注意:为连接u和v的边的权值,即dis[v],表示从起点到v点的最短路径的估计值,表示s到v的最短路径长度,就是的下界
P.S. 感觉似乎没有必要证明,想想就知道。。
(1)反证法证明最短路径的子路径也是最短路径
定理1 (最优子结构) 给定有向加权图G=(V,E),设P=<v1, v2,…, vk>为从结点v1到结点vk的一条最短路径,对任意i,j有i<=j<=k,设Pij=< vi, vi+1,…, vj>为从vi到vj的P的子路径,则Pij是从vi到vj的一条最短路径。
证明:我们把路径P分解为<v1,v2,…,vi,vi+1,…vj,…vk>。则w(P)=w(P1i)+w(Pij)+w(Pjk)。现在假设从vi到vj存在一路径P’ij,且w(P’ij)<w(Pij),则将P中的路径Pij=(vi,vi+1,…vj)替换成P’ij,依然是从v1到vk的一条路径,且其权值 w(P1i)+w(P’ij)+w(Pjk)小于w(P),这与前提P是从v1到vk的最短路径矛盾。(证毕)
(2)三角不等式性质
(3)上界性质:对于
(4)非路径性质:没有路径(注意不是边)相连的点到起点的最短路径为∞
(5)收敛性质:当达到下界时,任何relax操作都不会让它再发生变化
(6)路径松弛性质:对最短路径中的第k条边保证