算法学习(一)-----朱流算法

来源:互联网 发布:tcp udp 端口 编辑:程序博客网 时间:2024/05/21 18:48

前言
最近的学习,深深地感受到了算法分享的重要性。
应队友的要求,写一篇简单易懂的算法讲解(希望我写的出来),深入浅出(磨磨唧唧)的讲解一下朱刘算法。

什么是朱刘算法

大家应该都知道,如何求解最小生成树,可以使用Prim, 也可以使用Kruskal, 总而言之,就是寻找使得所有节点连通的权值最小的路径。
当然,这是基于无向图而言的,而对于有向图,我们要求“最小生成树”的话,就可以使用朱刘算法。

简而言之,朱刘算法就是求有向图的最小生成树。

大概想法:
用贪心的思想每次进行选择,缩点,最终求得。

重点内容

最小树形图构造流程

 1,选入边集——找到除root点之外,每一个点的所有入边中权值最小的,用数组in[]记录下这个最小权值,用pre[]记录到达该点的前驱;(若图中存在独立点,最小树形图是不存在的,所以在该步骤结束后,要判断一下)2,找有向环,并用数组id[]记录节点所属环的编号。3,找到环后,缩点,并更新权值。(感觉和SCC缩点差不多)4,以环数为下一次查找的点数,继续执行上述操作,直到没有环 或者 判定出不存在最小树形图为止。
建边:const int INF = 0x3f3f3f3f;const int MAXM = 40010;const int MAXN = 1010;struct Edge {  int u, v, cost;//u->v,边权值为w};Edge edge[MAXM];
int pre[MAXN];//存储父节点int id[MAXN];//id【i】记录节点i所在的环的编号int visit[MAXN];//标记作用int in[MAXN];//in【i】记录i入边中最小的权值
int zhuliu(int root, int n, int m)//朱刘算法, root是起始根节点,n是最大的点,m是边数{    int res = 0;//权值    int v;    while(1)    {        for(int i=0; i<n; i++)//遍历每一个点            in[i] = INF;//初始化in数组,将每一个入度的权值清为INF        for(int i=0; i<m; i++)//遍历每一条边        {            if(Edge[i].u!=Edge[i].v&&Edge[i].cost<in[Edge[i].v])//如果是一条边并且边权值小于入度的权值            {                pre[Edge[i].v] = Edge[i].u;//将前一个节点记录                in[Edge[i].v] = Edge[i].cost;//更新in数组            }        }        for(int i=0; i<n; i++)//遍历每一个点        {            if(i!=root&&in[i]==INF)//如果找到了一个入度的权值为INF,并且不是根节点            return -1;//则是没有找到最小树形图,则跳出循环        }        memset(id, -1, sizeof(id));//        memset(visit, -1, sizeof(visit));//        int tn = 0;        in[root] = 0;        for(int i=0; i<n; i++)//遍历每一个节点        {            res += in[i];//加入每一个点的最小入度的权值            int v = i;            while(visit[v]!=i&&id[v]==-1&&v!=root)//当加入的点不在环上,不是根节点            {                visit[v] = i;//标记                v = pre[v];//更新前一个节点            }            if(id[v]==-1&&v!=root)//如果当前节点都已经访问过了            {                for(int u=pre[v]; u!=v; u=pre[u])//找到之前的点                {                    id[u] = tn;//对id赋值                }                id[v] = tn++;//更新此值            }        }        if(tn==0)//没有可以构成的树            break;//跳出        for(int i=0; i<n; i++)//当结束了while循环,        {            if(id[i]==-1)//如果没有找到环的标号            {                id[i] = tn++;//每一个点都可以当成一个环            }        }        for(int i=0; i<m;)//遍历每一条边,展开环        {            v = Edge[i].v;//            int u = Edge[i].u;            Edge[i].u = id[Edge[i].u];            Edge[i].v = id[Edge[i].v];            if(Edge[i].u!=Edge[i].v)                Edge[i++].cost -= in[v];            else                swap(Edge[i], Edge[--m]);        }        n = tn;        root = id[root];    }    return res;}
原创粉丝点击