今天复习最小生成树算法。

最小生成树指的是在一个图中选择n-1条边将所有n个顶点连起来,且n-1条边的权值之和最小。形象一点说就是找出一条路线遍历完所有点,不能形成回路且总路程最短。

Kurskal算法

kurskal算法的核心思想是将边按权值排序,每次选出权值最小的边,只要不会形成回路就加入结果集,如果形成了回路就不选这条边,类似于贪心的思想。

具体做法是先将边按权值升序排序然后依次遍历,判断是否形成回路的方法是将点划分不同集合,初始状态每个点为一个集合,只有当一条边的两端分别位于两个集合时才选择这条边,否则就丢弃,这里用到了并查集来处理集合关系,可以参考这篇博文:https://www.cnblogs.com/czc1999/p/11823820.html,选择一条边之后要将两个点合并到同一集合。

模板题: https://www.luogu.com.cn/problem/P3366

代码如下,还是比较好理解的,时间复杂度为\(O(MlogM)\)。

#include <iostream>
#include <algorithm>
using namespace std; int n, m;
int pre[5005];
int Find(int x) { return pre[x] == x ? pre[x] : pre[x] = Find(pre[x]); } struct line
{
int from, to, val;
bool operator<(line a) { return val < a.val; }
}Arr[200005]; int Kruskal()
{
sort(Arr, Arr + m);
int cnt = n, res = 0;
for (int i = 0; i < m && cnt > 1; i++)
{
int x = Find(Arr[i].from), y = Find(Arr[i].to);
if (x != y)//x和y不在一个集合
{
pre[x] = y;//合并两个集合
cnt--;//找到了一条边
res += Arr[i].val;
}
}
return cnt == 1 ? res : -1;//如果cnt不等于1说明没找到n-1条边,无最小生成树
} int main() {
cin >> n >> m;
for (int i = 0; i < n; i++)pre[i] = i;
for (int i = 0; i < m; i++)cin >> Arr[i].from >> Arr[i].to >> Arr[i].val;
int res = Kruskal();
if (-1 == res)cout << "orz"; else cout << res;
return 0;
}

prim算法

Kruskal算法是选择边的思路,而prim算法通过选择点来得到最小生成树,有点类似于Dijkstra的感觉,初始源点可以任意选择,把点划分成已选择的点和未选择的点两个集合,需要维护一个dis数组代表每个点到已选择点的最短距离,不断把dis最小的未选择点加入已选择点集合然后更新dis,当所有点都变成已选择点(dis==0)的时候就得到了最小生成树。

代码如下,真的是跟Dijkstra很像了。

#include <iostream>
#include <algorithm>
using namespace std; #define inf 2000000000
int n, m;
int dis[5005];
int total = 0;
int head[5005], val[400005], to[400005], nextL[400005]; void AddLine(int a, int b, int c)
{
total++;
to[total] = b;
val[total] = c;
nextL[total] = head[a];
head[a] = total;
} int Prim()
{
int res = 0;
for (int i = 2; i <= n; i++)dis[i] = inf; for (int i = 0; i < n; i++)//循环n次找n个点
{
int Min = inf, u = 1;
for (int j = 1; j <= n; j++)//找下一个最近的未选择点
{
if (dis[j] != 0 && dis[j] < Min)
{
Min = dis[j]; u = j;
}
}
if (Min == inf && u != 1)return -1;//如果遍历之后未选择点dis都为inf,说明该图是非连通图
res += dis[u];
dis[u] = 0;
for (int j = head[u]; j; j = nextL[j])//更新该点周围的dis
{
if (dis[to[j]] > val[j])dis[to[j]] = val[j];
}
}
return res;
} int main() {
cin >> n >> m;
int a, b, c;
for (int i = 0; i < m; i++)
{
cin >> a >> b >> c;
AddLine(a, b, c);
AddLine(b, a, c);
}
int res = Prim();
if (-1 == res)cout << "orz"; else cout << res;
return 0;
}

堆优化

既然prim也是要每次取dis最小的点,当然也和Dijkstra一样可以用堆优化,朴素的prim时间复杂度为\(O(n^2)\),优化后达到\(O(nlogn)\),代码如下:

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std; #define inf 2000000000
int n, m;
int dis[5005];
int total = 0;
int head[5005], val[400005], to[400005], nextL[400005];
bool mark[5005]; void AddLine(int a, int b, int c)
{
total++;
to[total] = b;
val[total] = c;
nextL[total] = head[a];
head[a] = total;
} typedef pair<int, int> p;
priority_queue<p, vector<p>, greater<p>> q; int Prim()
{
int res = 0;
for (int i = 2; i <= n; i++)dis[i] = inf;
q.push(p(0, 1));
while (!q.empty())
{
int u=q.top().second,v=q.top().first;
q.pop();
if (mark[u])continue;
mark[u] = true;
dis[u] = 0;
res += v;
for (int i = head[u]; i ; i=nextL[i])
{
if (dis[to[i]] > val[i])
{
dis[to[i]] = val[i];
q.push(p(val[i], to[i]));
}
}
}
return res;
} int main() {
cin >> n >> m;
int a, b, c;
for (int i = 0; i < m; i++)
{
cin >> a >> b >> c;
AddLine(a, b, c);
AddLine(b, a, c);
}
int res = Prim();
if (-1 == res)cout << "orz"; else cout << res;
return 0;
}

所以选择使用哪个算法就看是点多还是边多了。

最小生成树算法总结(Kruskal,Prim)的更多相关文章

  1. 最小生成树模板【kruskal & prim】

    CDOJ 1966 Kruskal 解法 时间复杂度O(mlogm) m为边数,这里主要是边排序占时间,后面并查集还好 #include <cstdio> #include <cst ...

  2. 图-最小生成树算法之Kruskal及其Java实现

    1.Kruskal算法 Kruskal算法基于贪心,因此它追求的是近似最优解,也就是说由Kruskal得出的生成树并不一定是最优解. Kruskal算法求最小生成树的关键在于,每次选取图中权值最小(及 ...

  3. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  4. 最小生成树算法(Prim,Kruskal)

    边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...

  5. C++编程练习(10)----“图的最小生成树“(Prim算法、Kruskal算法)

    1.Prim 算法 以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树. 2.Kruskal 算法 直接寻找最小权值的边来构建最小生成树. 比较: Kruskal 算法主要是针对边来展开,边数 ...

  6. [数据结构]最小生成树算法Prim和Kruskal算法

    最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树.  例如,对于如上图G4所示的连通网可以有多棵权值总 ...

  7. 无向带权图的最小生成树算法——Prim及Kruskal算法思路

    边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...

  8. 最小生成树-Prim算法与Kruskal算法

    一.最小生成树(MST) ①.生成树的代价:设G=(V,E)是一个无向连通网,生成树上各边的权值之和称为该生成树的代价. ②.最小生成树:在图G所有生成树中,代价最小的生成树称为最小生成树. 最小生成 ...

  9. 最小生成树算法详解(prim+kruskal)

    最小生成树概念: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里 ...

随机推荐

  1. ROS中3D机器人建模(一)

    一,机器人建模的ROS软件包 *urdf : 机器人建模最重要的ros软件包是urdf软件包.这个软件包包含一个用于统一机器人描述格式(URDF)的C++解析器,它是一个表示机器人模型的XML文件,还 ...

  2. 树莓派4b点亮led灯基本步骤

    方法/步骤1: 首先要了解树莓派上的针脚,下面以树莓派4b为例子 把LED的正极插在GPIO脚上,把负极插在GND上 这里的例子是:正极插在GPIO21 方法/步骤2: 创建脚本 在配置好的树莓派系统 ...

  3. 场景6:具有OpenvSwitch的提供商网络

    此场景描述了使用带有Open vSwitch(OVS)的ML2插件的OpenStack网络服务的提供者网络实现. 在OpenStack网络引入分布式虚拟路由器之前,所有网络通信都通过一个或多个专门的网 ...

  4. selenium 环境配置

    一.确认系统中已安装python版本,如果没有,请参考[这里] 二.打开系统命令页面 [window + R].输入cmd回车 三.cmd环境下,用[pip install selenium ]命令安 ...

  5. 在 Vs2013中查看类的内部布局

    第一步:在Visual Studio中配置命令行环境(Command Prompt) 来自:https://blog.csdn.net/u013553529/article/details/77417 ...

  6. 动态主机配置协议-DHCP

    一.DHCP 概述 当局域网中有大量的PC时.如果我们逐个为每台PC去手动配置IP.那这就是一个吃力也未必讨好的办法 累死你 而DHCP 刚好可以解决这个问题.DHCP全称(动态主机配置协议).使用的 ...

  7. Flutter报错 Waiting for another flutter command to release the startup lock...

    Waiting for another flutter command to release the startup lock… 异常解决 平时我们在开发flutter过程中,在执行flutter p ...

  8. rc.local 启动内容不生效

    系统版本  CentOS Linux release 7.2.1511 问题 :/etc/rc.local  中的内容 启动机器后不生效 经过检查 /etc/rc.local 是 /etc/rc.d/ ...

  9. OpenCV图像变换二 投影变换与极坐标变换实现圆形图像修正

    投影变换 在放射变换中,物体是在二维空间中变换的.如果物体在三维空间中发生了旋转,那么这种变换就成为投影变换,在投影变换中就会出现阴影或者遮挡,我们可以运用二维投影对三维投影变换进行模块化,来处理阴影 ...

  10. 十天学会CS之操作系统——进程管理01

    进程管理01 进程的概念 进程是计算机中一个非常重要的概念,在整个计算机发展历史中,操作系统中程序运行机制的演变按顺序大致可以分为: 单道程序:通常是指每一次将一个或者一批程序(一个作业)从磁盘加载进 ...