参考资料:
https://www.cnblogs.com/hdu-zsk/p/8167687.html
https://www.luogu.com.cn/blog/xiaojiji/solution-p4716


一张无向图,你要用边权和最小的边来使所有点联通,显然用最小生成图。
现在的问题是,给你一张有向图并且钦定一个起点rrr,你要选边权和最小的边使rrr可以到达任意点。
这就是最小树形图问题。


上面的第一篇博客有演示过程,讲得比较详细。所以这里就随便胡一下:

  1. 对于rrr之外的每个点xxx,找到连向xxx的边权最小的边(自环除外),记为mnxmn_xmnx​。
  2. 如果存在点xxx满足没有这样的边连向它,那么它被孤立了,不可能有解。
  3. 将这些边都选出来,如果没有环,意味着最小树形图已经被找出来了;否则进入下一步的缩环操作。
  4. 对于每个在环中的点xxx,连向xxx的边中,如果起点不在这个环内,则用其边权减去连向mnxmn_xmnx​的边权。然后将整个环缩成一个点。
  5. 对于每个点xxx,将mnxmn_xmnx​的权值加入答案。

一直循环操作直到退出。
如果要得出具体的边是什么,稍微处理一下mnxmn_xmnx​应该就可以了(设连向xxx的边记为efxef_xefx​。在处理mnxmn_xmnx​的时候,由于xxx可能是被缩过的,找到mnxmn_xmnx​连向的具体的点yyy,然后覆盖efyef_yefy​。)

这还是挺容易感性理解的。
边权减去mnxmn_xmnx​的边权,因为mnxmn_xmnx​的权值已经加入答案了。如果后面要选择这条边,就应该要替换mnxmn_xmnx​。所以要用其边权减去mnxmn_xmnx​的边权。

这样时间复杂度是O(nm)O(nm)O(nm),因为每轮至少会少一个连通块。


模板

洛谷和LOJ上都有板题。

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 110
#define M 10010
#define INF 1000000000
int n,m,r;
struct edge{int u,v,w;} ed[M];
int pre[N],mn[N];
int id[N],cnt;
int vis[N];
int zhu_liu(){int res=0;while (1){for (int i=1;i<=n;++i)mn[i]=INF;for (int i=1;i<=m;++i){int u=ed[i].u,v=ed[i].v,w=ed[i].w;if (u!=v && w<mn[v])mn[v]=w,pre[v]=u;}for (int i=1;i<=n;++i)if (i!=r && mn[i]==INF)return -1;memset(vis,0,sizeof(int)*(n+1));memset(id,0,sizeof(int)*(n+1));cnt=0;for (int i=1;i<=n;++i){if (i==r)continue;res+=mn[i];int x=i;for (;vis[x]!=i && !id[x] && x!=r;x=pre[x])vis[x]=i;if (x!=r && !id[x]){id[x]=++cnt;for (int y=pre[x];y!=x;y=pre[y])id[y]=cnt;}}if (cnt==0)break;for (int i=1;i<=n;++i)if (!id[i])id[i]=++cnt;for (int i=1;i<=m;++i){int u=ed[i].u,v=ed[i].v,w=ed[i].w;ed[i]={id[u],id[v],w-(id[u]!=id[v]?mn[v]:0)};}n=cnt;r=id[r];}return res;
}
int main(){scanf("%d%d%d",&n,&m,&r);for (int i=1;i<=m;++i){int u,v,w;scanf("%d%d%d",&u,&v,&w);ed[i]=(edge){u,v,w};}printf("%d\n",zhu_liu());return 0;
}

Acceleration

上面第二篇博客有介绍,这里复述一下:
换一下写朱刘算法的姿势:
枚举点xxx,找与xxx相连的最小边mnxmn_xmnx​,将其加进来。如果这个时候出现了环,就像上面那样缩环并且修改外面连向环中的点的边的边权,重复操作。
考虑优化这个过程,给每个点开个左偏树,左偏树中存所有连向这个点的边。左偏树支持合并,并且这里还要支持对整个左偏树进行整体减。缩环的时候将环上所有点的左偏树进行整体减,然后合并到一起。
另外用并查集来维护每个具体的点被缩到了哪个点,还要用并查集来查是否出现环。详见代码。
时间复杂度变成了O((n+m)lg⁡m)O((n+m)\lg m)O((n+m)lgm)

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 110
#define M 1010
#define INF 1000000000
int n,m,r;
struct Node* null;
struct Node{Node *l,*r;int d;int u,w,tag;void gt(int c){w+=c,tag+=c;}void pd(){l->gt(tag),r->gt(tag),tag=0;};
};
Node *newnode(int u,int w){Node *nw=new Node;*nw={null,null,1,u,w,0};return nw;
}
Node *merge(Node *a,Node *b){if (a==null) return b;if (b==null) return a;if (a->w>b->w) swap(a,b);a->pd();a->r=merge(a->r,b);if (a->l->d<a->r->d)swap(a->l,a->r);a->d=a->r->d+1;return a;
}
void pop(Node *&t){if (t==null) return;Node *tmp=merge(t->l,t->r);delete t;t=tmp;
}
Node *in[N];
int fa[N],bel[N];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int getbel(int x){return bel[x]==x?x:bel[x]=getbel(bel[x]);}
int pre[N],mn[N];
int ZLA(){int res=0;for (int i=1;i<=n;++i)bel[i]=i,fa[i]=i;for (int i=1;i<=n;++i)if (i!=r){int x=getbel(i);while (1){while (in[x]!=null && getbel(in[x]->u)==x)pop(in[x]);if (in[x]==null)return -1;pre[x]=getbel(in[x]->u);res+=mn[x]=in[x]->wint y=pre[x];if (x!=getfa(y)){fa[x]=getfa(y);break;}in[x]->gt(-mn[x]);for (;y!=x;y=getbel(pre[y])){bel[y]=x;in[y]->gt(-mn[y]);in[x]=merge(in[x],in[y]);}}}return res;
}
int main(){//  freopen("in.txt","r",stdin);scanf("%d%d%d",&n,&m,&r);null=new Node;*null={null,null,0,0,0,0};for (int i=1;i<=n;++i)in[i]=null;for (int i=1;i<=m;++i){int u,v,w;scanf("%d%d%d",&u,&v,&w);in[v]=merge(in[v],newnode(u,w));}printf("%d\n",ZLA());return 0;
}

最小树形图——朱刘算法学习小记相关推荐

  1. poj3164(最小树形图朱刘算法模板)

    题目链接:http://poj.org/problem?id=3164 题意:第一行为n, m,接下来n行为n个点的二维坐标, 再接下来m行每行输入两个数u, v,表点u到点v是单向可达的,求这个有向 ...

  2. 最小树形图——朱刘算法

    洛咕博客地址:−>ClickHere<−->Click Here<-−>ClickHere<−,求捧场 最近想找最小生成树的题做,奈何难度有限,点进的蓝题紫题都和& ...

  3. 生成树最小树形图 -- 朱刘算法详解

    本文借鉴的博文: zephyr_pro dalao的blog 朱刘算法引入: (把一道最小树形图当作最小生成树来做了,wa了后以为是bug像个sb一样d了半天) 最小树形图和最小生成树都是要求总权值最 ...

  4. bzoj 4349: 最小树形图 朱-刘算法

    最裸的最小树形图(←现在才学的弱渣). 显然只需要打一下一个堡垒,然后剩下的可以最后用最小的代价再打. 然后只要把图建出来跑一下朱-刘算法即可. 简单讲一下朱-刘算法吧(思想还是很简单的),下面只考虑 ...

  5. 最小树形图-朱刘算法详解 +例题解析

    文章目录 最小树形图 定义 和最小生成树的区别 朱刘算法 思想 步骤 流程展示 算法实现 例题 POJ3164_Command_Network HDU2121_Ice_cream's_world_II ...

  6. NOIP模拟题 通讯 强连通分量缩点 最小树形图--朱刘算法

    通讯 (message.cpp\c\pas) [问题描述] "这一切都是命运石之门的选择." 试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短信,并由此得知了伦 ...

  7. [UVA - 11865]Stream My Contest(最小树形图+朱刘算法)

    本题过于简单,朱刘算法模板题,考虑二分一下带宽即可 title code title code #include <cstdio> #include <cstring> #in ...

  8. 最小树形图+朱刘算法

    大题上完整的朱.刘算法是由四个大步骤组成的: 1.求最短弧集合E 2.判断集合E中有没有有向环,如果有转步骤3,否则转4 3.收缩点,把有向环收缩成一个点,并且对图重新构建,包括边权值的改变和点的处理 ...

  9. 最小树形图(朱刘算法)

    不好意思 时间比较短,下面应该还会有修订的= = , 那段话是我复制过来的,觉得挺好的就用一下. 下面是讲解(不理解一的时候 , 可以看看二 ,结合图片): 一:   最小树形图,就是给有向带权图中指 ...

最新文章

  1. VML 画统计 柱状、饼图、折线
  2. 《高效程序员的修炼》 读书笔记
  3. 大数据+社会化协同 菜鸟不菜成老师
  4. 无心剑中译柒歌《爱的回音》
  5. rabbitmq AmqpClient 使用Fanout 交换机投递与接收消息,C++代码示例
  6. ASP.NET MVC学习系列 WebAPI初探
  7. leetcode[81]Search in Rotated Sorted Array II
  8. VMware虚拟机部署k8s集群
  9. jz2440裸机开发与分析:S3c2440代码重定位详解3---链接脚本的解析
  10. 学生信息管理系统作业
  11. 部署超级账本fabric区块可视化浏览器
  12. littlefs系列:Technical Specification
  13. KO data-bind=“click: func“函数自动执行问题
  14. 前端主流面试官必问超详细面试题(整理完以秃头)持续更新中
  15. 五年企稳上升的阿里,还能再涨吗?
  16. zabbix php ldap支持,安装zabbix时PHP ldap Warning
  17. 身体质量指数BMI——python
  18. HMM 隐马尔可夫模型
  19. python模拟键盘打字_python模拟鼠标点击和键盘输入的操作
  20. 化工原理物性参数_化工原理课程设计全套(包括图纸)

热门文章

  1. Python:一键更换桌面壁纸
  2. Java GUI气泡诗词02
  3. remix-ide,windows开发环境
  4. 计算机正确的坐姿教案,小学信息技术第二册 正确坐姿和指法—基本键打字练习教案...
  5. 读论文:SELFEXPLAIN: A Self-Explaining Architecture for Neural Text Classifiers
  6. Mac桌面壁纸文件如何提取里面的壁纸图片?heic动态桌面壁纸怎么导出jpg的图片形式?
  7. Freshman的插入排序实现
  8. 造成计算机系统不安全的因素,造成计算机系统不安全的因素包括()。
  9. POJ 3069 Saruman's Army(萨鲁曼军)
  10. 【我是渠道商】段丛斌:把产品当孩子来呵护,把客户当亲人来对待