图论总结

本来不想写图论算法的总结的,懒了,看了一会ZJU的题都**不会……还是老实写吧

 

1.最短路算法

 

1.1spfa算法

算是对bellon-ford算法的改进

改进就是只对松弛的点继续松弛

忘记spfa吧,没有过这个算法

 

1.2dijkstra算法

从起始点开始每次更新距离最近的点,然后再次更新到所有点的距离,每次贪心地取最近

由于已经取了最近的点,那么就不可能出现一条别的路径到这一点更近

由于是贪心做的,所以处理不了负边权问题

 

1.3floyd算法

 

floyd算法非常的优雅,只有短短的3个for循环,是一个经典的动态规划问题

d[i][j]表示从i到j的最短路,那么我们做n次松弛,第k次做代表的是

从i到j经过k条边的最短路径长度,但这并不能很好的解释为什么这么做是对的

这里提供另一种证明,传送门

我们只需要证明下面的结论

假设i到j最短路径上编号最大的节点为x

当k=x时,d[i][x],d[x][j]一定都是对应的最短路径的长度

我们不妨假设i到x路径上编号最大的节点为x1,x到j路径上编号最大的节点为x2

那么根据循环,如果该结论成立只需要当k=x1时d[i][x]最小,当k=x2时d[x][j]最小

那么归纳后只需要有边相连的俩个点长度时最小的即可,证明其正确性

 

1.3.1倍增floyd

 

floyd可以进行倍增操作

比如要求从起点s到终点t经过k条边的最短路

那么就可以将经过k/2条边的俩个矩阵合并成经过k条边的矩阵

这样就可以在$O(n^{3}*log_{2}k)$的时间代价内求出答案了

 

2.最小生成树算法

 

2.1.prim算法

 

prim是基于贪心的算法,维护一个数组d[i]表示把第i个点连入当前这棵树需要的最小代价

这样每次贪心选择d最小的点加入到树里面,再用这个点更新d数组即可

时间复杂度为$O(nm)$

 

2.2.kruskal算法

 

kruskal算法首先要对边进行排序,然后按照边权顺序尝试一条条加入新的边

由于按照边权由小到大的顺序加入,只要不加入多余的边一定得到的是最小生成树

同时要用并查集来维护已经构建好的若干棵树的合并问题

时间复杂度为$O(m*log_{2}m*\alpha(n))$

 

3.二分图匹配算法

 

3.1匈牙利算法

 

定义一个增广路的概念,即由原来的路径上再加一条边,使得原来连接的点仍然被边连接

我们只需要不断地去寻找是否存在新的增广路即可,由于n个顶点,那么我们就寻找n次

每次寻找的时候枚举从这个点连出去的边,然后尝试增广

如果连接的另外一个点尚未匹配,则直接相连,否则尝试对已经相连的点增广

若可以增广,再将这个点分过来

 

3.2KM算法

 

学弟说这个算法不用学……暂且信了吧

 

4.Tarjan算法

 

4.1强连通分量

 

定义有向图的强连通分量就是一个强连通分量的任意一个点都存在到另一点的路径

tarjan算法的思路是对有向图进行搜索,生成一颗搜索树,再考虑非树边对于连通块的影响

我们将每一个搜索到的节点打上时间戳标记dfn[i]搜索的顺序,用low[i]表示于第i个点相连的非树边能到达的节点的最小时间戳

搜索的时候如果是树边那么low[i]=min(low[i],low[to[i]])

如果是非树边那么low[i]=min(low[i],dfn[to[i]])

那么搜索的时候每次我们要获得一个连通块,所以再用sta模拟栈存一下,那么如果满足dfn[i]==low[i]说明从这个点开始无法沿着非树边回到之前已经搜索过的节点,意味着现在栈里面从栈顶一直到i节点构成一个强连通分量

 

4.2割点与割边

 

5.欧拉路径

 

欧拉路径是指从一个点出发经过每条边一次的路径

欧拉回路则是回到起点的欧拉路径

 

无向图存在欧拉路径当且仅当所有点的度数均为偶数,或者只有俩个度数为奇数的点

无相图存在欧拉回路当且仅当所有点度数均为偶数

 

有向图存在欧拉路径当且仅当所有点度数均为0,或者一个为1,一个为-1,其余为0

有向图存在欧拉回路当且仅当所有点度数均为0

 

寻找欧拉回路:从任意一个点开始遍历,走过的边进行标记

 

6.斯坦纳树

给定一张图,同时有若干个关键点,求使所有关键点连连通的最小生成树

做法是将关键点状压,$f[i][j]$表示以$i$为根的树,关键点连通性为$j$的最小代价

转移考虑俩种:

1.$f[i][j]=min(f[i][j],f[i][k]+f[i][l])$,$k+l=j,k\&l=0$

2.$f[i][j]=min(f[i][j],f[k][j]+val),edge(i,k)$

第一种转移直接做,第二种转移乱序不好做,但是发现与最短路相似

于是可以使用dijstra或者spfa(不推荐)进行转移

 

trick:如何方便高效地枚举子集呢?

1 for (int sub=(j-1)&j;sub;sub=(sub-1)&j)
View Code

 

posted @ 2018-06-16 14:57  2018szb  阅读(121)  评论(0)    收藏  举报