说到求图的最小环,比较常见的是Floyed求,复杂度为O(n^3),比较费时。再优一点的,枚举所有边,每次删去一条边(u, v),然后从u开始跑Dijkstra(用堆或者c++中优先队列优化的)求u到v的距离再加上(u, v)的权值,枚完玩所有边就得出答案了,复杂度为O(m * n * logn)。然而这样的复杂度还是不令人满意。
看了好多博客,倒是发现了一个比较优的方法,我们先求所给图的最小生成树,可以保证最小环就是由最小生成树上的边再加上一条非生成树上的边构成的,依次枚举所有边,每次加上一条边后,求出由其构成的环的权值,最后求最小值就解决了图的最小环问题。
再说说具体怎么求每次的环的权值,我们把图变成了一颗树,假设我们枚举了一条边(u, v), 我们只需要在生成树上求出u到v的距离在加上(u, v)这一条边的权值就算是求出环的权值了,至于求u到v的距离,用最近公共祖先(lca)可以解决,求出u和v到其公共祖先的距离之和就是u到v的距离了。
我们来分析一下其复杂度,求LCA的话,O(n)的复杂度的求深度等信息,O(nlogn)的复杂度递推求grand数组,O(logn)的复杂度求LCA,综合起来差不多就是O(nlogn)级别的复杂度;求最小生成树的复杂度为O(mlogm + nlogn),其中nlogn的复杂度是排序的复杂度。所以说整个求最小环的算法复杂度为O(nlogn + mlogm)级别的,效率算是比较高了。
复杂度算的有些懵,虽然网上说克鲁斯卡尔的复杂的为mlogm,但是我不太明白。如果又发现错误的地方欢迎指正。。。
具体代码实现如下:(对应于HDU - 6005)
#include
#include
#include
#include
#include