标签:
设G=(V,E)是连通的无向图,T是图G的一个最小生成树。如果有另一棵树T1,T1<>T,满足不存在树T’,T’<>T,w(T’)<w(T1),则称T1是图G的次小生成树。
存在边(u,v)T和(x,y)T满足T\(u,v)(x,y)是图G的次小生成树。
既然存在边(u,v)T和(x,y)T满足T\(u,v)(x,y)是图G的次小生成树, 那么所有的T\(u,v)(x,y)刚好构成了T的邻集,则T的邻集中权值最小的就是次小生成树了。但是如果我们每次枚举(u,v)和(x,y),还要判断能否构成一棵树,复杂度就太高了,至少是O(n*m)。效率比较高的做法是先加入(x,y),对于一棵树,加入(x,y)后一定会成环,如果删去环上除(x,y)以外的最大一条边,会得到加入(x,y)后的权值最小的一棵树。如果能快速计算出最小生成树中点x到y之间路径中最长边的长度,这个问题就很好的解决。最小生成树中x到y的最长边的长度可以用树形动态规划或者LCA等方法在O(n2)的时间复杂度内计算出,那么整个算法的时间复杂度就可以达到O(n2+n2+m)=O(n2)的时间复杂度内解决了。另外,如果用Kruskal算法求最小生成树,可以在算法的运行过程中求出x到y路径上的最长边,因为每次合并两个等价类的时候,分别属于两个等价类的两个点间的最长边一定是当前加入的边,按照这条性质记录的话就可以求出所有的值了。
在Kruskal算法的基础上进行修改,加入对x,y两点在最小生成树上路径中最长边的计算,存入length[][]数组。使用链式前向星记录每个集合都有哪些点。为了合并方便,除了head[]记录每条邻接表的头结点位置外,end[]记录每条邻接表尾节点的位置便于两条邻接表合并。Mst为最小生成树的权值,secmst为次小生成树的大小。在存在权值相同的边的情况下,secmst有可能等于mst。
Procedure Kruskal;
//初始化邻接表,对于每个节点添加一条指向其自身的边,表示以i为代表元的集合只有i
For i:=1 to n do
Begin
Link[i].to:=i;
Link[i].next:=-1;
End[i]:=i;
Head[i]:=i;
End;
Quick_sort(1,m);
For i:=1 to m do
Begin
If k=n-1 then break;
If e[i].w<0 then continue;
X:=father(e[i].a);
Y:=father(e[i].b);
If x<>y then
Begin
U:=head[x];
While u<>-1 do
Begin
V:=head[y];
While v<>-1 do
Begin
Length[link[u].to][link[v].to]:=e[i].w;
Length[link[v].to][link[u].to]:=e[i].w;
End;
End;
Link[end[x]].next:=head[y];
End[x]:=end[y];
Union(x,y); //注意,此处根据上两行的操作得 fa[y]:=x;
Inc(k);
E[i].select:=true;
End;
End;
//主程序
Begin
Kruscal;
Mst:=0;
For i:=1 to m do if e[i].select then inc(mst,e[i].w);
Secmst:=maxlongint;
For i:=1 to m do if not(e[i].select) setmst:=min(secmst,mst+e[i].w-length[e[i].a][e[i].b]);
End;
标签:
原文地址:http://www.cnblogs.com/cxvdzxhb/p/4478827.html