最小树形图

最小树形图:即有向图的最小生成树。

最小树形图的第一个算法是1965年朱永津和刘振宏提出的复杂度为O(VE)的算法。

有向图的最小生成树与无向图的最小生成树的区别:对于无向图,只要该图的最小生成树存在,那么无论以哪个点为根,一定可以找到其最小生成树;而对于有向图来说,可能从某一个顶点为根开始找最小生成树是存在的,而从另一个点为根来找却不能找到一个最小生成树。

最小树形图问题的分类:一般分为两类,有定根的最小树形图和无定根的最小树形图。有定根最小树形图就是题目给出谁是固定的根,然后从这个固定的根开始找一颗最小生成树,不定根最小树形图,题目不给出固定的根,让你求一棵最小生成树。

最小树形图的求解:不论是有定根还是无定根,其求解的步骤都是相同的,唯一不同的是求不定根最小树形图我们需要建立虚根,并添加多余的边。在此我们求解最小树形图的采用的方法是国产算法,朱刘算法,其他求解方法有tarjan。

图1

求解步骤:
(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值