ACM第四专题—图算法总结

   图的基本知识 
   顶点:图中的数据元素称为顶点.
有向图:有方向的图叫有向图.
无向图:没有方向的图叫无线图.
完全图:有n(n-1)/2条边的无向图称为完全图.
有向完全图:具有n(n-1)条弧的有向图称为有向完全图.
稀疏图:有很少条边或弧的图称为稀疏图,反之称为稠密图.
权:与图的边或弧相关的数叫做权(weight).


1. Relaxation(松弛操作): 
procedure relax(u,v,w:integer);//多数情况下不需要单独写成procedure。 
begin 
  if dis[u]+w<dis[v] then 
    begin 
      dis[v]:=dis[u]+w; 
      pre[v]:=u; 
    end 
end; 
2. Dijkstra 
1) 适用条件&范围: 
a) 单源最短路径(从源点s到其它所有顶点v); 
b) 有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图) 
c) 所有边权非负(任取(i,j)∈E都有Wij≥0); 
2) 算法描述: 
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s}; 
b) For i:=1 to n 
1.取V-S中的一顶点u使得dis[u]=min{dis[v]|v∈V-S} 
2.S=S+{u} 
3.For V-S中每个顶点v do Relax(u,v,Wu,v) 
c) 算法结束:dis[i]为s到i的最短距离;pre[i]为i的前驱节点 
3) 算法优化: 
使用二叉堆(Binary Heap)来实现每步的DeleteMin(ExtractMin,即算法步骤b中第1步)操作,算法复杂度从O(V^2)降到O((V+E)㏒V)。推荐对稀疏图使用。 
使用Fibonacci Heap(或其他Decrease操作O(1),DeleteMin操作O(logn)的数据结构)可以将复杂度降到O(E+V㏒V);如果边权值均为不大于C的正整数,则使用Radix Heap可以达到O(E+V㏒C)。但因为它们编程复杂度太高,不推荐在信息学竞赛中使用。 
3. Floyd-Warshall 
1) 适用范围: 
a) APSP(All Pairs Shortest Paths) 
b) 稠密图效果最佳 
c) 边权可正可负 
2) 算法描述: 
a) 初始化:dis[u,v]=w[u,v] 
b) For k:=1 to n 
For i:=1 to n 
For j:=1 to n 
If dis[i,j]>dis[i,k]+dis[k,j] Then
Dis[I,j]:=dis[I,k]+dis[k,j]; 
c) 算法结束:dis即为所有点对的最短路径矩阵 
3) 算法小结: 
此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法。时间复杂度O(n^3)。 
考虑下列变形:如(I,j)∈E则dis[I,j]初始为1,else初始为0,这样的Floyd算法最后的最短路径矩阵即成为一个判断I,j是否有通路的矩阵。更简单的,我们可以把dis设成boolean类型,则每次可以用“dis[I,j]:=dis[I,j]or(dis[I,k]and dis[k,j])”来代替算法描述中的蓝色部分,可以更直观地得到I,j的连通情况。 
与Dijkstra算法类似地,算法中蓝色的部分可以加上对Pre数组的更新,不再赘述。 
4. Prim (Dijksta的推广)
1) 适用范围: 
a) MST(Minimum Spanning Tree,最小生成树) 
b) 无向图(有向图的是最小树形图) 
c) 多用于稠密图 
2) 算法描述: 
a) 初始化:dis[v]=maxint(v∈V,v≠s); dis[s]=0; pre[s]=s; S={s};tot=0 
b) For i:=1 to n 
1.取顶点v∈V-S使得W(u,v)=min{W(u,v)|u∈S,v∈V-S,(u,v)∈E} 
2.S=S+{v};tot=tot+W(u,v);输出边(u,v) 
3.For V-S中每个顶点v do Relax(u,v,Wu,v) 
c) 算法结束:tot为MST的总权值 
注意:这里的Relax不同于求最短路径时的松弛操作。它的代码如下: 
procedure relax(u,v,w:integer);        //松弛操作 
begin 
  if w<dis[v] then 
    begin 
      pre[v]:=u; 
      dis[v]:=w; 
    end; 
end; 
可以看到,虽然不同,却也十分相似。 
3) 算法优化: 
使用二叉堆(Binary Heap)来实现每步的DeleteMin(ExtractMin)操作 
算法复杂度从O(V^2)降到O((V+E)㏒V)。推荐对稀疏图使用。 
使用Fibonacci Heap可以将复杂度降到O(E+V㏒V),但因为编程复杂度太高,不推荐在信息学竞赛中使用。 
(不要问我为什么和Dijkstra一样……观察我的prim和dijkstra程序,会发现基本上只有relax和输出不一样……) 
5. Kruskal 
1) 适用范围: 
a) MST(Minimum Spanning Tree,最小生成树) 
b) 无向图(有向图的是最小树形图) 
c) 多用于稀疏图 
d) 边已经按权值排好序给出 
2) 算法描述: 
基本思想:每次选不属于同一连通分量(保证无圈)且边权值最小的2个顶点,将边加入MST,并将所在的2个连通分量合并,直到只剩一个连通分量 
3) 算法实现: 
a) 将边按非降序排列(Quicksort,O(E㏒E)) 
b) While 合并次数少于|V|-1 
i. 取一条边(u,v)(因为已经排序,所以必为最小) 
ii. If u,v不属于同一连通分量 then 
1) 合并u,v所在的连通分量 
2) 输出边(u,v) 
3) 合并次数增1;tot=tot+W(u,v) 
c) 算法结束:tot为MST的总权值 
4) 分析总结: 
检查2个顶点是否在同一连通分量可以使用并查集实现(连通分量看作等价类)。 
我们可以看到,算法主要耗时在将边排序上。如果边已经按照权值顺序给出,那太棒了…… 
另外一种可以想到的实现方法为:O(n)时间关于边权建二叉小根堆;每次挑选符合条件的边时使用堆的DelMin操作。这种方法比用Qsort预排序的方法稍微快一些,编程复杂度基本一样。附程序。 
另外,如果边权有一定限制,即<=某常数c,则可以使用线性时间排序以获得更好的时间效率。

你可能感兴趣的:(ACM第四专题—图算法总结)