描述:最小树形图—-一个有向图的有根生成树中权最小的图。
原理:朱刘算法及相关定理。
代码 :
#include<bits\stdc++.h>
using namespace std;
typedef int CostType;
struct Edge{
int u;
int v;
CostType cost;
Edge(){};
Edge(int x,int y,CostType c):u(x),v(y),cost(c){};
};
#define MV 1005
#define ME 100005
#define Max INT_MAX
CostType liu_zhu(Edge* E,int vn,int em,int root){
/* 该算法求一个图G的最小树形图。
E是图中边的集合。
vn是图中点的个数,编号依次为0,1,2……n-1。
em为边集E的大小。
root为指定的根。
*/
/*
算法包括几个部分:求最小入弧集,找孤立点,找圈,缩点。
*/
CostType min_in[vn];//记录最小入弧的大小。
int pre[vn];//记录最小入弧的关联点。
int vis[vn];//找圈时记录被那个点访问过。
int new_id[vn];//缩点时新的编号。
int min_cost=0;//返回结果。
memset(min_in,Max,vn);
memset(pre,-1,vn);
while(true){
for(int i=0;i<em;i++)//找最小入弧集,排除自环。
if(E[i].u!=E[i].v&&E[i].cost<min_in[E[i].v]){
min_in[E[i].v]=E[i].cost;
pre[E[i].v]=E[i].u;
}
int now_new_id=0;
for(int i=0;i<vn;i++)//找孤立点.
if(i!=root&&pre[i]==-1)
return -1;
memset(vis,-1,vn);
memset(new_id,-1,vn);
min_in[root]=0;
for(int i=0;i<vn;i++){
min_cost+=min_in[i];
/* 最小树形图除顶点外一定每一点都有一条入弧,将最小值加入结果。
然后重新编号时将所有入弧都减去最小值。
*/
int v;
for(v=i;v!=root&&vis[v]!=i&&new_id[v]==-1;v=pre[v]){
/*在最小弧集中找圈。从i点开始往前找,直到找到根(无圈),
或者找到一个之前访问过的点(即一个圈)。
*/
vis[v]=i;
}
if(v!=root&&new_id[v]==-1){
//有一个新圈,将圈中元素编号。
for(int u=v;new_id[u]==-1;u=pre[u])
new_id[u]=now_new_id;
now_new_id++;
}
}
if(now_new_id==0) break;//若没有圈,结束。
for(int i=0;i<vn;i++)//对圈外其他点赋予新编号。
if(new_id[i]==-1)
new_id[i]=now_new_id++;
for(int i=0;i<em;i++){
/*更新编号,同时,更新权。最后,所有边权为的边构成最小树形图。
有可能某些点有多条权为0的边,保留其中一条不改变连通性的边即可。
*/
int v=E[i].v;
E[i].v=new_id[E[i].v];
E[i].u=new_id[E[i].u];
E[i].cost-=min_in[v];
}
vn=now_new_id;
root=new_id[root];//最后,更新节点数和根节点编号。
}
return min_cost;
}