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

本文主要是介绍朱刘算法(Directed Minimum Spanning Tree/Directed MST/Minimum Arborescence/Optimum Branchings),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概念

最小树形图:有向图所分离出的有向生成树,亦称为最小树形图,其应满足以下条件:
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 doublestruct _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

这篇关于朱刘算法(Directed Minimum Spanning Tree/Directed MST/Minimum Arborescence/Optimum Branchings)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/396476

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

最大公因数:欧几里得算法

简述         求两个数字 m和n 的最大公因数,假设r是m%n的余数,只要n不等于0,就一直执行 m=n,n=r 举例 以18和12为例 m n r18 % 12 = 612 % 6 = 06 0所以最大公因数为:6 代码实现 #include<iostream>using namespace std;/