【树论 2】Kruskal 的学习和使用
Tips:本题解是【随便搞搞 1】Prim算法的学习和使用 的姊妹篇,希望先阅读Prim算法。
预习及预备知识:
克鲁斯卡尔(Kruskal)算法是实现图的最小生成树最常用的算法。
大家知道,存储图的方法有2种:邻接矩阵表示法、邻接表表示法;
这里介绍的是介于这两种之间的一种方法:边接存储法(即直接用边来存储图)
但是最小生成树是什么呢?
标准定义如下:在边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
听起来非常的带劲,我们就一起来探讨这一求最小生成树的算法!
Kruskal算法的三大特征:
●对于稠密图中求最小生成树优于Prim算法
●对于稀疏图中求最小生成树的时间复杂度O(n+m log m)
●标准Kruskal算法流程包含并查集的部分知识
算法思想:
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。
时间复杂度为为O(e^2), 使用并查集优化后复杂度为 O(eloge),与网中的边数有关,适用于求边稀疏的网的最小生成树。
克鲁斯卡尔算法从另一途径求网的最小生成树。假设连通网N=(V,{E}),则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{∮}),图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。
图例1:
引入集合到集合的距离:对于两个集合A,B;集合A中元素和B中元素各不相同
集合A中所有元素到集合B中所有元素的距离最小值定义为集合到集合的距离
贪心方法:每一次只要连接集合到集合距离最小的两个集合,反复n-1次得出的为最小生成树
对于上例,图为依照克鲁斯卡尔算法构造一棵最小生成树的过程。代价分别为1,2,3,4的四条边由于满足上述条件,则先后被加入到T中,代价为5的两条边(1,4)和(3,4)被舍去。因为它们依附的两顶点在同一连通分量上,它们若加入T中,则会使T中产生回路,而下一条代价(=5)最小的边(2,3)联结两个连通分量,则可加入T。因此,构造成一棵最小生成树。
图例2:
a图中是输入的一个无向图;
b中连接集合到集合距离最小的两个集合1 3,{1,3}就是一棵最小生成树;
c图连接4 6,此时图中有两个元素大于等于2个的连通分支{1,3}{4,6}分别是最小生成树;
d图连接2 3,此时图中有三个元素大于等于2个的连通分支{1,3}{4,6}{2,5}分别是最小生成树;
e图连接两个连通分支{1,3}{4,6},图中有两个元素大于等于2个的连通分支{2,5}{1,3,4,6}分别是最小生成树;
f图连接两个连通分支{2,5}{1,3,4,6},图中有一个元素大于等于2个的连通分支{1,2,3,4,5,6}是最小生成树;
算法完成连通分支{1,2,3,4,5,6}就是关于全图a的最小生成树;
模板分析:
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz
输入输出格式
输入格式:
第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)
接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi
输出格式:
输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz
输入输出样例
输入样例#1:
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出样例#1:
7
说明
时空限制:1000ms,128M
数据规模:
对于20%的数据:N<=5,M<=20
对于40%的数据:N<=50,M<=2500
对于70%的数据:N<=500,M<=10000
对于100%的数据:N<=5000,M<=200000
样例解释:
所以最小生成树的总边权为2+2+3=7
【分析】
对于样例,我们把1 2 3 4四点抽象为含有一个元素的子集{1}{2}{3}{4}
首先看到子集{1}{3}之间距离最小,且遍历越靠后,合并形成一个连通分支{1,3}{2}{4}
看到子集{2}{1,3}之间距离最小,合并形成一个连通分支{1,2,3}{4}
看到子集{1,2,3}{4}之间距离最小,合并形成一个连通分支{1,2,3,4}
看到连通分支{1,2,3,4}是全局的最小生成树,所以路径和为2+2+3=7
【实现】
type rec=record
u,v,len:longint;
end;
var n,m,i:longint;
a:array[..]of rec;
f:array[..]of longint;
procedure qsort(l,r:longint);//按照边权从大到小排序
var i,j,mid:longint;
t:rec;
begin
i:=l; j:=r; mid:=a[(l+r)div ].len;
repeat
while a[i].len<mid do inc(i);
while a[j].len>mid do dec(j);
if i<=j then begin
t:=a[i]; a[i]:=a[j]; a[j]:=t;
inc(i); dec(j);
end;
until i>=j;
if j>l then qsort(l,j);
if i<r then qsort(i,r);
end;
function father(x:longint):longint;//求x的father,学过的都看得懂
begin
if f[x]=x then exit(x);
f[x]:=father(f[x]);
exit(father(f[x]));//加了路径压缩,快一点
end;
procedure kruskal;
var tot,cnt,i,fx,fy:longint;
begin
for i:= to m do f[i]:=i;//并查集赋初值
qsort(,m);//快速排序
cnt:=;
tot:=;//初值
for i:= to m do begin//遍历每条边
fx:=father(a[i].u);
fy:=father(a[i].v);//求一条边的左右两个端点的father
if fx<>fy then begin//判断如果不是同一father则这两个点在不同的子树中
f[fx]:=fy;//合并
inc(cnt);//连了这条边u<--->v
tot:=tot+a[i].len;//加上这条边的权值
end;
if cnt=n- then break;//加到n-1就跳
end;
if cnt<>n- then writeln('orz')//不符合最小生成树的边=点数-1就是不合法
else writeln(tot);//输出各边权值和
end;
begin
readln(n,m);
for i:= to m do readln(a[i].u,a[i].v,a[i].len);
kruskal;
end.
时间复杂度的推导:
初始化 所有点各自为一个森林 这一步是O(n)的
并且把边集进行从小到大排序,这一步如果使用快速排序或者堆排序是O(mlogm)
然后在这一片森林中添加边,我们知道n个点构成的树是有n-1条边,因此需要执行n-1次以下操作
从已经排序的边序列中,挑选长度最短的,且两端不在同一棵树中的一条边,判断是否是同一棵树是利用并查集进行查询,挑出这一条边之后,把两个端点代表的树合并为一棵,即并查集的合并,这也是O(1)的
注意到在选取边的过程中,只要挑选其中的n-1条,因此挑选边的n-1次挑选边的复杂度之和是O(m)的(可以理解为看最后一条连接的边在序列的第几条,最坏情况就是最后一条才能使n个点连通,因此最坏复杂度是O(m)
因此总复杂度为O(n+mlogm)
应用:
有了这一个神奇的算法我们能干什么呢?
村庄修路,网络铺设,关于最小生成树的问题,应用比较广,学科范围广,这里给一个模板题:
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 6623 | Accepted: 3608 |
Description
Your task is to design the network for the area, so that there is a connection (direct or indirect) between every two points (i.e., all the points are interconnected, but not necessarily by a direct cable), and that the total length of the used cable is minimal.
Input
The maximal number of points is 50. The maximal length of a given route is 100. The number of possible routes is unlimited. The nodes are identified with integers between 1 and P (inclusive). The routes between two points i and j may be given as i j or as j i.
Output
Sample Input
Sample Output
参见上面的程序,prim注意有重边的处理,但是Kruskal不用担心重边的问题
【树论 2】Kruskal 的学习和使用的更多相关文章
- 设备树(device tree)学习笔记
作者信息 作者:彭东林 邮箱:pengdonglin137@163.com 1.反编译设备树 在设备树学习的时候,如果可以看到最终生成的设备树的内容,对于我们学习设备树以及分析问题有很大帮助.这里我们 ...
- 设备树(device tree)学习笔记【转】
转自:https://www.cnblogs.com/pengdonglin137/p/4495056.html 阅读目录(Content) 1.反编译设备树 2.分析工具fdtdump 3.Linu ...
- P4197 Peaks [克鲁斯卡尔重构树 + 主席树][克鲁斯卡尔重构树学习笔记]
Problem 在\(Bytemountains\)有\(n\)座山峰,每座山峰有他的高度\(h_i\) .有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个困难值,这个值越大表示越难走, ...
- 【浏览器渲染原理】渲染树构建之渲染树和DOM树的关系(转载 学习中。。。)
在DOM树构建的同时,浏览器会构建渲染树(render tree).渲染树的节点(渲染器),在Gecko中称为frame,而在webkit中称为renderer.渲染器是在文档解析和创建DOM节点后创 ...
- 回文树/回文自动机(PAM)学习笔记
回文树(也就是回文自动机)实际上是奇偶两棵树,每一个节点代表一个本质不同的回文子串(一棵树上的串长度全部是奇数,另一棵全部是偶数),原串中每一个本质不同的回文子串都在树上出现一次且仅一次. 一个节点的 ...
- 树堆(Treap)学习笔记 2020.8.12
如果一棵二叉排序树的节点插入的顺序是随机的,那么这样建立的二叉排序树在大多数情况下是平衡的,可以证明,其高度期望值为 \(O( \log_2 n )\).即使存在一些极端情况,但是这种情况发生的概率很 ...
- BZOJ3551 Peaks加强版 [Kruskal重构树,主席树]
BZOJ 思路 我觉得这题可持久化线段树合并也可以做 我觉得这题建出最小生成树之后动态点分治+线段树也可以做 还是学习一下Kruskal重构树吧-- Kruskal重构树,就是在做最小生成树的时候,如 ...
- 主席树初步学习笔记(可持久化数组?静态区间第k大?)
我接触 OI也快1年了,然而只写了3篇博客...(而且还是从DP跳到了主席树),不知道我这个机房吊车尾什么时候才能摸到大佬们的脚后跟orz... 前言:主席树这个东西,可以说是一种非常畸形的数据结构( ...
- 基于设备树的controller学习(2)
作者 彭东林 pengdonglin137@163.com 平台 TQ2440 Linux-4.10.17 概述 上一篇大概介绍了一下demo-controller的结构,下面结合驱动分析. 正文 ...
随机推荐
- SQLAlchemy并发写入引发的思考
背景 近期公司项目中加了一个积分机制,用户登录签到会获取登录积分,但会出现一种现象就是用户登录时会增加双倍积分,然后生成两个积分记录.此为问题 问题分析 项目采用微服务架构,下图为积分机制流程 ...
- IEEE1588 ( PTP ) 协议简介
IEEE1588 协议,又称 PTP( precise time protocol,精确时间协议),可以达到亚微秒级别时间同步精度,于 2002 年发布 version 1,2008 年发布 vers ...
- uiimageview 的 animation 动画
NSMutableArray *meiArr = [NSMutableArray arrayWithCapacity:4]; for (int i = 0; i < 4; i++) { NSSt ...
- nice和renice命令详解
基础命令学习目录首页 进程调度是linux中非常重要的概念.linux内核有一套高效复杂的调度机制,能使效率极大化,但有时为了实现特定的要求,需要一定的人工干预.比如,你希望操作系统能分配更多的CPU ...
- flume handler
1.classpath classpath中需要这两项:Flume Agent configuration file and the second are the Flume client jars ...
- JS进阶系列之闭包
刚刚总结完作用域链,我觉得很有必要马上对闭包总结一下,因为,之前也写过自己对闭包的理解,那时候只知道,闭包就是可以访问别的函数变量的函数,就是在函数里面的函数就叫做闭包,可是并没有深入探究,为什么,可 ...
- java把map转json
JSONUtils.toJSONString(requestMap); com.alibaba.fastjson.JSON <!-- https://mvnrepository.com/a ...
- MathExam任务一
小学一二年级数学计算题 一.预估与实际 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 60 35 • Es ...
- 【CSAPP笔记】10. 代码优化
写程序的主要目标是使它在所有可能的情况下都能正确运行(bug free),一个运行得很快但有 bug 的程序是毫无用处的.在 bug free 的基础上,程序员必须写出清晰简洁的代码,这样做是为了今后 ...
- beta冲刺7/7
队名:Boy Next Door 燃尽图 代码写入 https://github.com/mangoqiqi/paybook/tree/master/Desktop/Web%E8%B4%A6%E5%8 ...