最小树形定义:在向图中,通过某一个定点v,能访问到所有其它点,且使权值和最小。
本文介绍的算法叫朱刘算法,是由两位中国人朱永津,刘振宏在1965所发明。算法的核心思想是除了定点外,每个点必有一条入边,贪心及缩点。它的步骤是:
step1:从v点出发,检测是否能访问所有顶点。能,执行step2;否则不存在最小树形图,返回。
step2:对于除定点v之外的所有点,找出它的最小入边pre[i]及其权值in[i];
step3:对于step2找出的所有边,检测是否存在环,不存在,则最小权值和为sum{in[i]},1 <= i <= vertex(点的总和),返回;否则,执行step4;
step4:将环上的点缩成一个新点,用这个新点代替原来环上所有的点。而对于所有的点i,修改其入边值costi = costi - in[i];返回step2。
模板是根据hh的改的http://www.notonlysuccess.com/index.php/mst/
struct Graph
{
#define N 101
#define M 10001
#define INF 100000000
int vertex,edge;
int head[N];
struct Node
{
//u --> v
int u;
int v;
int next;
int cost;
Node(){};
Node(int a,int b,int c,int d):u(a),v(b),next(c),cost(d){};
}adjlist[M];
//valuables min_tree needs
int visited[N];
int in[N],pre[N],color[N];
int mincost,count;
void init(int n)
{
memset(head,-1,sizeof(head));
vertex = n;
edge = 0;
}
void insert(int u,int v,int w)
{
adjlist[edge] = Node(u,v,head[u],w);
head[u] = edge++;
}
int min_tree(int root)
{
//检测从根能否遍历整个图
memset(visited,0,sizeof(visited));
check(root);
for(int i = 1;i <= vertex;i++)
if(!visited[i])
return -1;
mincost = 0;
while(1)
{
//更新最小入边
for(int i = 1;i <= vertex;i++)
in[i] = INF;
for(int i = 0;i < edge;i++)
{
int u = adjlist[i].u;
int v = adjlist[i].v;
int w = adjlist[i].cost;
if(in[v] > w && u != v) //u != v
{
in[v] = w;
pre[v] = u;
}
}
//查环
count = 0;
in[root] = 0;
memset(visited,0,sizeof(visited));
memset(color,0,sizeof(color));
color[root] = ++count;
for(int i = 1;i <= vertex;i++)
{
mincost += in[i];
int v = i;
while(visited[v] != i && !color[v])
{
visited[v] = i;
v = pre[v];
}
//对环染色
if(!color[v])
{
//注意,这里v是环上的点,但i不一定是
color[v] = ++count;
for(int u = pre[v];u != v;u = pre[u])
color[u] = count;
}
}
//无环
if(count == 1)
return mincost;
//非环上的点重新设编号
for(int i = 1;i <= vertex;i++)
if(!color[i])
color[i] = ++count;
//缩点
for(int i = 0;i < edge;i++)
{
int u = adjlist[i].u;
int v = adjlist[i].v;
adjlist[i].u = color[u];
adjlist[i].v = color[v];
if(color[u] != color[v])
adjlist[i].cost -= in[v];
}
vertex = count;
root = color[root];
}
return -1;
}
void check(int x)
{
visited[x] = 1;
for(int i = head[x];i != -1;i = adjlist[i].next)
{
int v = adjlist[i].v;
if(!visited[v])
check(v);
}
}
}g;
这是定根的,如果不定根的话,我们可以加一个虚拟结点,它到每个点加一个边,权值为所有权值和 + 1或者更大,设其为INF。在最后返回判断的时候,min - INF < INF,则图产有生成树有权值和最小为min - INF;否则,没有生成树。不定根的时候,我们可以知道,因为新增的点到所有点都有边,所以可以略去step1。
另外,不定根输出定点的时候,可以在结构体Node里面加两个属性realu,realv,表示它实际的点,而我们计算的时候(比如缩点)用的则u和v。