最小树形图:即有向图的最小生成树。
最小树形图的第一个算法是1965年朱永津和刘振宏提出的复杂度为O(VE)的算法。
有向图的最小生成树与无向图的最小生成树的区别:对于无向图,只要该图的最小生成树存在,那么无论以哪个点为根,一定可以找到其最小生成树;而对于有向图来说,可能从某一个顶点为根开始找最小生成树是存在的,而从另一个点为根来找却不能找到一个最小生成树。
最小树形图问题的分类:一般分为两类,有定根的最小树形图和无定根的最小树形图。有定根最小树形图就是题目给出谁是固定的根,然后从这个固定的根开始找一颗最小生成树,不定根最小树形图,题目不给出固定的根,让你求一棵最小生成树。
最小树形图的求解:不论是有定根还是无定根,其求解的步骤都是相同的,唯一不同的是求不定根最小树形图我们需要建立虚根,并添加多余的边。在此我们求解最小树形图的采用的方法是国产算法,朱刘算法,其他求解方法有tarjan。
求解步骤:
(1) 先求出每个顶点的最小入边权,如果图无环,且入度为0的顶点至多有一个,则最小树形图就是所有最小入边权的和。如果入度为0的点多余1个,则不存在最小树形图,算法结束,若存在有向环则继续执行步骤2。
(2) 如果存在有向环,把位于一个环中的点缩成一个节点,并对图中所有节点进行重新编号,且对新图的边进行更新。形成新图,
(3) 重复步骤(1),(2)直到图中无环,算法结束,答案是每一次循环中所有点的最小入边权累加结果即为答案。
代码:
int Directed_MST(int root,int V,int E)
{//传入的参数分别为根节点,顶点数目,边数目
int ans=0;
int u,v;
while(1)
{
for(int i=0; i<=V; i++)
minIn[i]=INF;///minIn存放i节点的最小入边权
for(int i=0; i<E; i++)
{///找最小入边权
u=edge[i].u;
v=edge[i].v;
if(minIn[v]>edge[i].w&&u!=v)
{
minIn[v]=edge[i].w;
pre[v]=u;
if(u==root)
pos=i;
}
}
for(int i=0; i<V; i++)
{///如果存在root以外的孤立点,则不存在最小树形图
if(i==root)continue;
if(minIn[i]==INF) return -1;
}
memset(vis,-1,sizeof(vis));
memset(id,-1,sizeof(id));///id[i]存放节点i的新编号
int cnt=0;
minIn[root]=0;
for(int i=0; i<V; i++)
{
ans+=minIn[i];
v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)
{///判是否有环,有环回到自身,无环的话就会回到根
vis[v]=i;
v=pre[v];
}
if(id[v]==-1&&v!=root)
{///有环,把环中的编号都赋为一样
for(u=pre[v]; u!=v; u=pre[u])
id[u]=cnt;
id[v]=cnt++;
}
}
if(cnt==0) break;
for(int i=0; i<V; i++)
if(id[i]==-1)
id[i]=cnt++;///标记孤立点
for(int i=0; i<E; i++)
{
u=edge[i].u;
v=edge[i].v;
edge[i].u=id[u];
edge[i].v=id[v];
if(id[u]!=id[v])
{///不在一个环中
edge[i].w-=minIn[v];
}
}
V=cnt;
root=id[root];
}
return ans;
}