首页 > 其他分享 >最小生成树(Kruskal & Prim)

最小生成树(Kruskal & Prim)

时间:2023-03-04 11:44:33浏览次数:52  
标签:dist int Kruskal 最小 生成 Prim

最小生成树

定义

对于一个带权连通无向图G=(V,E),生成树不同,每棵树的权(树中所有边上的权值和)也不同,设R为G的所有生成树的集合,若T为R中权值和最小的生成树,则T称为G的最小生成树(Minimum-Spanning-Tree,MST)

1、最小生成树可能有多个,但边的权值之和总是唯一且最小的
2、最小生成树的边数=定点数-1,砍掉一条则不连通,增加一条则会出现回路
3、若一个连通图本身就是一颗树,则其最小生成树就是它本身
4、只有连通图才有生成树,非连通图只有生成森林

如何求最小生成树?

两种算法:Kruskal算法和Prim算法。

Kruskal算法

在一个点集中,通过贪心的思想去找边权从小到大的边。这样,我们可以保证生成的树的边权之和最小。(证明请移步其他博客)

再通过并查集去查看两个点是否已经加入了生成树中(祖先是否相同)。如果已经在生成树中,则不加入这条边。这条边不仅仅是多余的,而且还不是最小值(前面加入最小生成树的边一定小)。

最后,判断是否能够生成一棵树。每一次合并两个点代表生成了一条边,故有n个点的点集应该有n-1条边。若没有,则说明这不是一棵有所有点的最小生成树。

由于Kruskal是使用的并查集,故对于稀疏图比较友好。

所以,它的时间复杂度为O(|n|log|n|)。

Prim算法

从一个点开始往外扩展,通过贪心的思想从小到大去扩展每一个点的连边。过程类似于BFS,每一次扩展一的点的所有边,找到权值最小的那一条。

写法也非常类似Dijkstra,甚至堆优化都和Dijkstra几乎一样。

由于时间复杂度较高:O(|n|^2),因此多用于稠密图。

Kruskal和Prim的区别

Kruskal算法适用于稀疏图(并查集有类似离散化的效果),而且时间复杂度相对较低,且是从点集中选取两点之间最短的边来建树。

Prim算法适用于稠密图,时间复杂度相对较高,且是从一个点开始用类似BFS的方式贪心扩展每一个点(边权值从小到大),直到扩展完所有的边。

代码

模板题:P3379 【模板】最近公共祖先(LCA)

Prim算法:

因为是数据范围是5000,开个5000*5000的二维表问题不大。干就完事了。

朴素版Prim

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f

using namespace std;

const int N = 5e3 + 10;

int g[N][N];
int dist[N];
bool st[N];
int n, m;

void prim()
{
	memset(dist, inf, sizeof dist);
	int res = 0;
	dist[1] = 0;
	
	for (int i = 1; i <= n; i++)
	{
		int t = -1;
		
		for (int j = 1; j <= n; j++)
			if (!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;
				
		if (dist[t] == inf)
		{
			puts("impossible");
			return;
		}
		
		res += dist[t];
		st[t] = true;
		
		for (int j = 1; j <= n; j++)
			if (!st[j] && dist[j] > g[t][j])
				dist[j] = g[t][j];
	}
	
	cout << res << endl;
}

int main()
{
	memset(g, inf, sizeof g);
	
	scanf("%d%d", &n, &m);
	
	for (int i = 1; i <= m; i++)
	{
		int a, b, w;
		scanf("%d%d%d", &a, &b, &w);
		g[a][b] = g[b][a] = min(g[a][b], w);
	}
	
	prim();
	
	return 0;
}

Kruskal算法

一定要记得初始化并查集fa[]数组!排序是按边权从小到大排序!

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f

using namespace std;

const int N = 2e5 + 10;

struct edge
{
	int a, b, w;
	
	bool operator < (const edge &W) const
	{
		return w < W.w;
	}
};

edge e[N];
int fa[N];
int n, m, a, b, w;

int find(int x)
{
	if (fa[x] == x)
		return x;
	
	return fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	
	if (fx != fy)
		fa[fx] = fy;
}

int kruskal()
{
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	
	sort(e + 1, e + 1 + m);
	
	int res = 0, cnt = 0;
	
	for (int i = 1; i <= m; i++)
	{
		int a = e[i].a, b = e[i].b, w = e[i].w;
		
		a = find(a), b = find(b);
		
		if (a != b)
		{
			fa[a] = b;
			cnt++;
			res += w;
		}
	}
	
	if (cnt != n - 1)
		return inf;
	else
		return res;
}

int main()
{
	scanf("%d%d", &n, &m);
	
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d%d", &a, &b, &w);
		e[i] = {a, b, w};
	}
	
	int t = kruskal();
	
	if (t == inf)
		puts("orz");
	else
		printf("%d\n", t);
	
	return 0;
}

标签:dist,int,Kruskal,最小,生成,Prim
From: https://www.cnblogs.com/GTGumiiL/p/17177982.html

相关文章

  • kmp 与最小循环节
    题目:一个字符串的前缀是从第一个字符开始的连续若干个字符,例如abaab共有5个前缀,分别是a,ab,aba,abaa,abaab。我们希望知道一个N位字符串S的前缀是否具有循环节。......
  • linux最小化安装后添加图形界面
    linux支持图形界面,如果装成了最小化系统该怎样添加图形界面呢?首先要知道的是,要想从最小化安装到图形界面绝对要装很多包,我们不可能一个包一个包的去装,大多数小伙伴应该也记......
  • linux最小化安装后添加图形界面
    linux支持图形界面,如果装成了最小化系统该怎样添加图形界面呢?首先要知道的是,要想从最小化安装到图形界面绝对要装很多包,我们不可能一个包一个包的去装,大多数小伙伴应该也记......
  • pat乙级1023 组个最小数
    #include<stdio.h>#include<stdlib.h>#include<string.h>#include<math.h>intmain(){inta[10];for(inti=0;i<10;i++){scanf......
  • Ed25519 use process (primary)
    GenerateKey、Signopenurl:https://cyphr.me/ed25519_applet/ed.html私钥签名公钥验签Verifyopenurl:https://ed25519.altr.dev/......
  • 深度学习导论知识——最小二乘法(Ordinary Least Squares, OLS)
    1.知识点简介最小二乘法(OrdinaryLeastSquares,OLS)是常见的估计模型参数的方法。早在19世纪,勒让德就认为按照“误差的平方和最小”这个规则估计出来的模型是最接近......
  • OpenCloudOS 如何以最小成本,高效定位内存泄露路径?
    导读|遭受内存泄露往往是令开发者头疼的问题,传统分析工具gdb、Valgrind在解决内存泄露问题上效率较低。本文特别邀请到了OpenCloudOS社区Contributor、腾讯后台开发工程......
  • 代码随想录day17| 二叉树的最大深度 二叉树的最小深度 完全二叉树的节点个数
    二叉树的最大深度给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。分析层序遍历每层depth++classSolution{p......
  • lc.209 长度最小的子数组
    题目给定一个含有n个正整数的数组和一个正整数target。找出该数组中满足其和≥target的长度最小的连续子数组[numsl,numsl+1,...,numsr-1,numsr],并返回其......
  • [Primer] 第 7 章
    第7章函数——C++的编程模块7.1复习函数的基本知识C++函数不能直接返回数组,但是可以把数组当成对象的组成部分返回。函数将返回值复制到指定的CPU寄存器或内存......