朱刘算法(Directed Minimum Spanning Tree/Directed MST/Minimum Arborescence/Optimum Branchings)

2023-05-16

概念

最小树形图:有向图所分离出的有向生成树,亦称为最小树形图,其应满足以下条件:
1. 恰好有一个入度为0的点,称为根结点
2. 其他结点的入度均为1
3. 可以从根结点到达其他结点

既然要找最小生成树,当然就是找权重越小的边越好。每一个点(除了根以外)各自找到权重最小的入边之后,有可能就刚好是一棵最小生成树了,但是也有可能形成几只“水母”。

由于每个点都仅有一条入边,如果形成环,环上一定只有出边,不会有入边。每个点都仅有一条入边,除了刚好形成一棵树以外,要不就是形成水母 —— 一只环再加上环上的点各是一棵树的树根,或者说是很多棵树的树根用环串起。

水母图

水母与最小树形图

最小生成树不能有环,所以水母是不合格的,然而水母是权重最小的连接方式。若有一棵恰当的最小生成树,其权重会略高于水母。由水母向最小生成树靠拢,可能有以下两种情况:

  1. 改变水母环上的边,让水母变成一棵树,尽管整体权重稍微变大,但仍可接受。
  2. 改变水母触手上的边,并没有比较好。不但让整体权重变大,而且水母环仍存在,并没有解决掉不合格的问题。

由此可得出结论:

只需要尝试打开水母环上的边就行了。打开环的时候,要同时考虑新加入的边的权重和取消的边的权重。选择差值最小者,可让权值增加最小。进入水母的环全部看一遍后,就能选出差值最小者。

朱刘算法(Chu-Liu/Edmonds Algorithm)

算法思想

根据Kruskal’s Algorithm提到的最小生成树相连性质,可以知道连接多只水母,就和连接多棵最小生成树的道理是一样的,以权重小的边來连接是最好的。唯一不同的是,Kruskal’s Algorithm一旦发现造成环的边,就直接舍弃;Chu-Liu/Edmonds Algorithm则是留下造成环的边(形成水母),并且尝试各种打开环的方式。

该算法设计思想巧妙,也较难理解。 Sasuke_SCUT 的blog讲原理解释得较清楚,特摘录如下:

判断是否存在树形图的方法很简单,只需要以v为根作一次图的遍历就可以了,所以下面的 算法中不再考虑树形图不存在的情况。
在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进行了这步操作,总算法复杂度才真正能保证是O(VE)。
首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。
上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w’(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。
如果实现得很聪明的话,可以达到找最小入边O(E),找环 O(V),收缩O(E),其中在找环O(V)这里需要一点技巧。这样每次收缩的复杂度是O(E),然后最多会收缩几次呢?由于我们一开始已经拿掉了所有的自环,我门可以知道每个环至少包含2个点,收缩成1个点之后,总点数减少了至少1。当整个图收缩到只有1个点的时候,最小树形图就不不用求了。所以我们最 多只会进行V-1次的收缩,所以总得复杂度自然是O(VE)了。由此可见,如果一开始不除去自环的话,理论复杂度会和自环的数目有关。

chu_liu and kruskal

算法步骤

  1. 找到除了root以为外其他点的权值最小的入边。用In[i]记录 ;
  2. 如果出现除了root以为存在其他孤立的点,则不存在最小树形图。;
  3. 找到图中所有的环,并对环进行缩点,重新编号。;
  4. 更新其他点到环上的点的距离 ;
  5. 重复3,4直到没有环为止。

chu_liu Algorithm

Codes

// 摘自:http://blog.csdn.net/ditian1027/article/details/21958821

#include <iostream>
#include <math.h>
#include <cstdio>
using namespace std;

#define N 105
#define INFINITE 999999999
#define MYTYPE double

struct _point
{
    int x;
    int y;
} point[N];

struct _edge
{
    int from;
    int to;
    MYTYPE cost;
} edge[N*N];

MYTYPE inw[N];  //最小入边
int vis[N]; //是否被访问 
int id[N];  //由当前图到重构图的映射 
int pre[N]; //前驱顶点

MYTYPE Directed_MST(int root, int NV, int NE)
{
    MYTYPE ret=0;
    while (1)   //开始迭代过程 
    {
        //1.确定最小入边集 
        for(int i=0; i<NV; ++i) inw[i]=INFINITE;
        for (int i=0; i<NE; ++i)
        {
            int from=edge[i].from;
            int to=edge[i].to;
            if (edge[i].cost<inw[to] && from!=to)   //from!=to忽略自环
            {
                inw[to]=edge[i].cost;
                pre[to]=from;
            }
        }
        //检查是否有不可达点
        for (int i=0; i<NV; ++i)
        {
            if(i==root) continue;   //除根之外
            if(inw[i]==INFINITE) return -1; //有不可达顶点,不可能生成最小树形图,退出
        }
        //2.找环
        for (int i=0; i<NV; ++i)
        {
            vis[i]=-1;
            id[i]=-1;
        }
        int newidx=0;
        inw[root]=0;
        for (int i=0; i<NV; ++i)    //有两个作用:计算最小入边集的权值和;检查是否有环,如果有,重新对点进行编号
        {
            ret+=inw[i];
            int v=i;
            while (vis[v]!=i && id[v]==-1 && v!=root)   
            //由v回溯。能回到根,即最后v==root,那么肯定不在环里;回不到根,v!=root,v有可能在环里,也有可能不在(回溯到一个环然后出不去了,同样也到不了根)。
            //若v在环里,则环上所有点的id[]值会被重新标号,不再是-1;若是后一种情况,它前驱的环上的点的id[]已被修改为非-1,不能通过“id[v]==-1”这个条件的检查。
            {
                vis[v]=i;
                v=pre[v];
            }
            if (v!=root && id[v]==-1)   //两个条件保证了:1.在环上2.这环没被处理过
            {
                //下面把环上所有的点的标号设置为同一个  
                for (int u=pre[v]; u!=v; u=pre[u])
                {
                    id[u]=newidx;
                }
                id[v]=newidx;
                ++newidx;
            }
        }
        if(newidx==0)   break;  //无环,ret就是答案,跳出迭代
        for (int i=0; i<NV; ++i)
        {
            if(id[i]==-1) id[i]=newidx++;   //给环外的点继续编号
        }
        //3.重新构图,准备下一次迭代
        for (int i=0; i<NE; ++i)
        {
            int to=edge[i].to;
            edge[i].from=id[edge[i].from];
            edge[i].to=id[edge[i].to];
            if(edge[i].from != edge[i].to)
            {
                edge[i].cost -= inw[to];    //算法的关键
            }
        }
        //为下一轮迭代赋初值
        NV=newidx;
        root=id[root];
    }
    return ret;
}

MYTYPE calcdist(int point_a, int point_b)
{
    MYTYPE delta_x=(MYTYPE)(point[point_a].x - point[point_b].x);
    MYTYPE delta_y=(MYTYPE)(point[point_a].y - point[point_b].y);
    return sqrt(delta_x*delta_x + delta_y*delta_y);
}

int main()
{
    int n, m, x, y, from, to;
    MYTYPE ans;
    while (scanf("%d", &n) != EOF)
    {
        scanf("%d", &m);
        for (int i=0; i<n; ++i) //n个顶点
        {
            scanf("%d%d", &x, &y);
            point[i].x=x;
            point[i].y=y;
        }
        for (int i=0; i<m; ++i) //m个边
        {
            scanf("%d%d", &from, &to);
            edge[i].from=from-1;
            edge[i].to=to-1;
            edge[i].cost=calcdist(from-1, to-1);
        }
        ans=Directed_MST(0, n, m);

        if (ans==-1)    printf("NO\n");
        else printf("%.2f\n", ans);
    }
    return 0;
}

参考:演算法笔记-Tree

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

朱刘算法(Directed Minimum Spanning Tree/Directed MST/Minimum Arborescence/Optimum Branchings) 的相关文章

  • Haskell - 如何基于二叉树的foldr创建mapTree函数?

    这是 Haskell 编程的第一原理 第 11 章代数数据类型中的一个问题 data BinaryTree a Leaf Node BinaryTree a a BinaryTree a deriving Eq Ord Show 我们实际上
  • 如何防止 Ext Js 树中检查更改时的 itemclick 事件

    我在 Ext tree Panel 中添加了两个侦听器 检查更改 和 项目单击 但我注意到 当检查发生更改时 它也会触发项目单击事件 我希望阻止该项目的点击事件 listeners checkchange function node che
  • 用 C++ 解释 2D 线段/四叉树 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 附 这可能不是重复的 我进行了搜索 并确保我没有得到我想要的东西 我是一名 ACM 问题解决者 最近我学习了线性数组的线段树和具有延迟传播的
  • 在Python中生成字典树中的所有叶到根路径

    我有一个 非标准 形式的字典树 如下所示 tree 0 A B C D E F 叶节点被定义为字典键值对 其中值是空字典 我想将所有叶到根路径提取为列表列表 如下所示 paths C B A 0 E D 0 F D 0 如果有帮助的话 也可
  • 没有重复子项的树

    Using anytree https pypi python org pypi anytree我制作了这样的树 A B C D F B C E G 有没有办法删除所有重复的子级并将其变成下面的树 对所有可能级别的子级进行递归 A B C
  • 递归地循环遍历对象(树)

    有没有办法 在 jQuery 或 JavaScript 中 循环遍历每个对象及其子对象和孙对象等等 如果是这样 我也能读到他们的名字吗 Example foo bar child grand greatgrand and so on 所以循
  • 使用STL的红黑树内部实现

    我知道我的STL g 4 x x附带 使用红黑树来实现地图等容器 是否可以直接使用STL内部的红黑树 如果是这样 怎么办 如果不是 为什么不 为什么STL不公开红黑树 令人惊讶的是 我无法使用谷歌找到答案 编辑 我正在研究使用红黑树作为插入
  • 总结树上的值

    我使用树控件来查看一些基于嵌套 父子 表的分层项目 每个节点都有一个 NameValue 格式 接受 name 和 value 但只有叶子 最后一个节点 具有整数值 并且父节点的值保留为空 仅是它们具有的名称 我想总结值 以便每个父节点都保
  • B 树和 2-3-4 树之间的区别

    B 树和 2 3 4 树有什么区别 另外 你如何找到每个的最大和最小高度 链接到维基百科 http en wikipedia org wiki 2 3 4 tree and引用 2 3 4 树是 4 阶 B 树 A 2 3 4 is a B
  • Java 中的树实现

    我得到了以下树 然后我们被告知使用last child previous sibling方法来改变这三个的实现 结果如下 我现在正在研究 Java 实现 以在这棵树上执行不同的功能 我们有一个 Tree 接口和一个 TreeNode 接口
  • 树 /f cmd 修改日期。 Windows Powershell

    我需要创建一个 tree驱动器的文件夹 子文件夹和修改日期的列表 tree f命令效果很好 但我需要添加修改日期 我怎样才能做到这一点 它不会那么漂亮 但你可以使用 Recurse使用 Get ChildItem 选项来查找所有这些 此外
  • Swift 4 - 设置最低部署目标

    当前的部署目标是 11 0 这很好 但是 我想知道如何设置最小值 8 0 您可以在项目目标的常规设置中设置部署目标 您可以在 Apple 文档中阅读更多相关信息 设定部署目标 https developer apple com librar
  • 如何使用 php 列出目录以在文件夹中导航,而不使用 javascript? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在寻找这个 PHP 函数 列出目
  • 创建二叉树的时间复杂度

    我正在尝试从提供的源创建一棵树 要添加到树中的 2 个节点 以及应添加这 2 个新闻节点的节点 为了找到该节点在树中的位置 我使用了中序遍历 该遍历的时间复杂度为 O n 因此 如果要在树中添加 n 个节点 则创建整个树的时间复杂度为 O
  • 命令提示符中树的输出

    我希望能够使用 tree F A gt desktop file txt 命令仅输出文本文件 目前 它输出每个文件扩展名 有谁知道有一个简单的方法可以做到这一点 Tree仅接受几个命令行参数 c gt Tree Graphically di
  • 二叉搜索树是平衡的吗?

    这已经讨论过了here https stackoverflow com questions 742844 how to determine if binary tree is balanced 但我在下面有一个实现 线程中从未讨论过 pub
  • 单击父节点时检查树的子节点 [ExtJS]

    我想知道如何在单击 ExtJs 中的特定节点时检查树的同级节点 我已经给了每个节点的 id 我可以访问单击的节点的 id 那么我如何继续自动检查子节点 有人请帮助我 or any other way of getting hands on
  • 提取给定节点的所有父节点

    我正在尝试使用以下命令提取每个给定 GO Id 节点 的所有父级EBI RDF sparql 端点 https www ebi ac uk rdf services sparql 我是根据this https stackoverflow c
  • 如何使用 XSLT 从平面 XML 列表构建树?

    我使用极简 MVC 框架 其中PHP控制器手上的DOM模型 to the XSLT 视图 c f okapi http okapi liip ch 为了构建导航树 我在 MYSQL 中使用了嵌套集 这样 我最终得到一个如下所示的模型 XML
  • Silverlight 中的图形可视化

    我有一个表示有向图的数据结构 我正在寻找一个好的 Silverlight 可视化 以允许我从一个节点导航到另一个节点 最好带有一些漂亮的动画 有谁知道这种显示有什么好的 UI 控件或框架吗 甚至是来自另一个领域的样本 也许是社交网络 我的图

随机推荐

  • MySQL show关键字用法

    SHOW DATABASES 列出 MySQL Server 上的数据库 SHOW TABLES FROM db name 列出数据库中的表 SHOW TABLE STATUS FROM db name 列出数据库的表信息 xff0c 比较
  • windows10共享文件夹挂载到Ubuntu

    程序开发人员一般都会把开发目录放在windows系统下 xff0c 开发环境却是linux 以前我是linux下文件挂载到windows xff0c 有同事前车之鉴 xff0c 万一虚拟机linux挂壁了 xff0c 很难恢复 现在准备把w
  • 闭包函数中use使用

    匿名函数中的use xff0c 其作用就是从父作用域继承变量 下例是最常见的用法 xff0c 如果不使用use xff0c 函数中将找不到变量 msg 1 2 3 4 5 6 7 8 lt php msg 61 1 2 3 func
  • for update秒杀

    Mysql InnoDB 排他锁 用法 xff1a select for update 例如 xff1a select from goods where id 61 1 for update 排他锁的申请前提 xff1a 没有线程对该结果集
  • UNION 和 UNION ALL

    UNION用的比较多union all是直接连接 xff0c 取到得是所有值 xff0c 记录可能有重复 union 是取唯一值 xff0c 记录没有重复 UNION 和 UNION ALL 的语法都是 xff1a SQL 语句 1 UNI
  • php网站压测(ab)

    一般来说核心页面都需要进行压测 xff0c 特别是秒杀页面 xff0c 从而知道网站的承受能力 xff0c 方便暴露一些问题 xff0c 更好的把控网站 压测工具有很多种 xff0c 最简单 方便的可以使用ApacheBench xff0c
  • CSV文件读取 C++版本

    代码 span class token comment 创建结构体 xff0c 把读取数据可以放入结构体成员中 span span class token keyword struct span span class token class
  • 四种常见的 POST 提交数据方式

    HTTP 1 1 协议规定的 HTTP 请求方法有 OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT 这几种 其中 POST 一般用来向服务端提交数据 xff0c 本文主要讨论 POST 提交数据
  • json_decode

    json 61 34 34 errorno 34 0 34 errormsg 34 34 可以 34 34 data 34 34 guid 34 34 5762340 34 34 username 34 34 wiu370468 34 34
  • csv乱码处理

    handle 61 fopen 34 war csv 34 34 r 34 row 61 1 while data 61 fgetcsv handle 1000 34 34 data 61 eval 39 return 39 iconv 3
  • OR和AND关键字一起使用的情况

    OR和AND关键字一起使用的情况 OR关键字和AND关键字 xff0c 可以一起使用 xff0c 需要注意 xff0c AND的优先级高于OR 因此 xff0c 当两者一起使用时 xff0c 应该先运算AND两边的条件表达式 xff0c 再
  • Ubuntu cron 定时执行任务

    cron xff0c 是一个Linux定时执行工具 xff0c 可以在无需人工干预的情况下运行作业 1 关于crontab 在Ubuntu server 下 xff0c cron是被默认安装并启动的 通过 etc crontab文件 xff
  • Linux服务器上监控网络带宽的18个常用命令

    本文介绍了一些可以用来监控网络使用情况的Linux命令行工具 这些工具可以监控通过网络接口传输的数据 xff0c 并测量目前哪些数据所传输的速度 入站流量和出站流量分开来显示 一些命令可以显示单个进程所使用的带宽 这样一来 xff0c 用户
  • Linux系统使用iftop查看带宽占用情况

    Linux系统下如果服务器带宽跑满了 xff0c 查看跟哪个ip通信占用带宽比较多 xff0c 可以通过iftop命令进行查询 xff0c 使用方法如下 xff1a 1 安装方法 软件官网地址 xff1a http www ex parro
  • linux基础命令

    1 curl amp wget 使用curl或wget命令 xff0c 不用离开终端就可以下载文件 如你用curl xff0c 键入curl O后面跟一个文件路径 wget则不需要任何选项 下载的文件在当前目录 代码如下 curl O we
  • find_in_set

    1 in查询相当于多个or条件的叠加 xff0c 例如 xff1a select from user where user id in 1 2 3 等效于 select from user where user id 61 1 or use
  • 集成Cortex-M0内核-- Integration and Implementation Manual手册学习

    根据使用场景 xff0c 配置并集成一个Cortex M0的内核 xff0c 暂时不涉及的实现的部分 目录 阅读手册 Chapter1 Introduction 1 1 About the processor 1 2 About integ
  • 在NVIDIA NX 配置OpenCV多版本冲突和解决的总结

    Nvidia Jetson NX 环境 直接刷JetPack5 1的镜像 xff0c 会得到如下环境 Ubuntu20 04cuda11 4TensorRT8 4cudnn8 4opencv4 5 4 而且这些源一般是从nv xxxx等源下
  • 一款入门级的飞控CC3D(一)

    很多在校的朋友想自己动手做一款旋翼无人机 xff0c 但是零件采购下来要花费不少大洋 xff0c 装配完成后又需要进行软件硬件调试 所以很多想做极客的梦就扼杀在摇篮里 本期我将开始更新一款入门级的飞控CC3D 首先 xff0c 放上CC3D
  • 朱刘算法(Directed Minimum Spanning Tree/Directed MST/Minimum Arborescence/Optimum Branchings)

    概念 最小树形图 xff1a 有向图所分离出的有向生成树 亦称为最小树形图 xff0c 其应满足以下条件 xff1a 1 恰好有一个入度为0的点 xff0c 称为根结点 2 其他结点的入度均为1 3 可以从根结点到达其他结点 既然要找最小生