次小生成树

次小生成树

定义

设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;

posted @ 2015-05-05 12:50  no regrets  阅读(225)  评论(0编辑  收藏  举报