• 次小生成树的两种算法


    一、“换边”算法

    用Kruskal求最小生成树,标记用过的边。求次小生成树时,依次枚举用过的边,将其去除后再求最小生成树,得出所有情况下的最小的生成树就是次小的生成树。可以证明:最小生成树与次小生成树之间仅有一条边不同。

    这样相当于运行m次Kruskal算法。

    复杂度O(m^2)

    示例代码:

    int Kruskal_MinTree()
    {
        int u,v;
        init();
        int i,flag,cnt;
        minedge = 0;
        flag = cnt = 0;
        int tmp = 0;
        for(i=0;i<m;i++)
        {
            u = edge[i].s;
            v = edge[i].t;
            int fx = findset(u);
            int fy = findset(v);
            if(fx != fy)
            {
                use[minedge++] = i;
                tmp += edge[i].w;
                fa[fx] = fy;
                cnt++;
            }
            if(cnt == n-1)
            {
                flag = 1;   //找到最小生成树
                break;
            }
        }
        if(flag)
            return tmp;
        return -1;   //不存在最小生成树,返回-1
    }
    
    int Sec_MinTree()
    {
        int i,j,mini,tmp;
        int cnt,flag,u,v;
        mini = Mod;
        for(i=0;i<minedge;i++)   //枚举最小生成树中的每条边,将其去除
        {
            init();
            flag = cnt = tmp = 0;
            for(j=0;j<m;j++)
            {
                if(j != use[i])  //去除边
                {
                    u = edge[j].s;
                    v = edge[j].t;
                    int fx = findset(u);
                    int fy = findset(v);
                    if(fx != fy)
                    {
                        cnt++;
                        tmp += edge[j].w;
                        fa[fx] = fy;
                    }
                    if(cnt == n-1)
                    {
                        flag = 1;
                        break;
                    }
                }
            }
            if(flag && tmp < mini)
                mini = tmp;
        }
        if(mini == Mod)
            mini = -1;
        return mini;
    }
    View Code

    二、“最长边”法

    算法流程:先加入(x,y),对于一棵树,加入(x,y)后一定会形成环,如果删去环上除(x,y)外最大的一条边,会得到加入(x,y)时权值最小的一棵树,如果能快速计算最小生成树中点x与点y之间路径中最长边的长度,即可快速解决。

    复杂度O(mlogm+n^2)

    示例代码:

    int fa[N];
    const int M = 100010
    struct Edge
    {
        int u,v,w;
        bool chose;
    }edge[M];
    
    struct Adge
    {
        int v,next;
    }G[N];        //边数组
    
    int tot,n,m;
    int first[N];  //邻接表头节点位置
    int end[N];    //邻接表尾节点位置
    int mp[N][N];  //任意两点在最小生成树路径上的最长边长
    
    
    int findset(int x)
    {
        if(x != fa[x])
            fa[x] = findset(fa[x]);
        return fa[x];
    }
    
    int cmp(Edge ka,Edge kb)
    {
        if(ka.w != kb.w)
            return ka.w < kb.w;
        if(ka.u != kb.u)
            return ka.u < kb.u;
        return ka.v < kb.v;
    }
    
    void Kruskal(Edge *edge)
    {
        int k = 0;
        int i,w,v;
        //初始化邻接表,对于每个节点添加一条指向其自身的边,表示以i为代表元的集合只有点i
        for(tot=0;tot<n;tot++)
        {
            G[tot].v = tot+1;
            G[tot].next = first[tot+1];
            end[tot+1] = tot;
            first[tot+1] = tot;
        }
        sort(edge+1,edge+m+1,cmp);
        for(i=1;i<=m;i++)
        {
            if(k == n-1)
                break;
            if(edge[i].w < 0)
                continue;
            int fx = findset(edge[i].u)
            int fy = findset(edge[i].v);
            if(fx != fy)   //修改部分,遍历两个点所在集合
            {
                for(w=first[fx];w!=-1;w=G[w].next)
                {
                    for(v=first[fy];v!=-1;v=G[v].next)
                    {
                        //每次合并两个等价类的时候,分别属于两个等价类的两点间的最长边一定是当前加入的边
                        mp[G[w].v][G[v].v] = mp[G[v].v][G[w].v] = edge[i].w;
                    }
                }
                //合并两个邻接链表
                G[end[fy]].next = first[fx];
                end[fy] = end[fx];
                fa[fx] = fy;
                k++;
                edge[i].chose = 1;
            }
        }
    }
    
    int main()
    {
        //初始化建图
        int mst,secmst;
        Kruskal(edge);
        mst = 0;
        for(i=1;i<=m;i++)
            if(edge[i].chose)
                mst += edge[i].w;
        secmst = INF;
        for(i=1;i<=m;i++)
        {
            if(!edge[i].chose)
            {
                secmst = min(secmst,mst+edge[i].w-mp[edge[i].u][edge[i].v]);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Emacs和ESS的使用技巧。
    响应式
    Day learn,day up
    Docker快速安装kafka | 沈健的技术博客
    闭包函数如何使用循环变量
    leetcode笔记——35.搜索插入位置
    CSS 之动态变换背景颜色
    吴裕雄 PYTHON 神经网络——TENSORFLOW 双隐藏层自编码器设计处理MNIST手写数字数据集并使用TENSORBORD描绘神经网络数据2
    吴裕雄 PYTHON 神经网络——TENSORFLOW 双隐藏层自编码器设计处理MNIST手写数字数据集并使用TENSORBORD描绘神经网络数据
    吴裕雄 PYTHON 神经网络——TENSORFLOW 单隐藏层自编码器设计处理MNIST手写数字数据集并使用TensorBord描绘神经网络数据
  • 原文地址:https://www.cnblogs.com/whatbeg/p/3775281.html
Copyright © 2020-2023  润新知