Kruskal算法浅显的讲解

前言

本文 不同步 发表于Luogu Blog

正文

前言*2

最小生成树的定义非常浅显,就是边权值最小的一棵树(给定我们好几种连边方式,来求最小生成树)

求最小生成树有好几种算法

    1. 适用于稠密图的Prim算法(太弱了还没学)
    1. 适用于稀疏图的Kruskal算法(比较简单所以拿捏了)
    1. 没有人知道但是被大佬提过一嘴的 Borůvka (Sollin) 算法(实际上是Prim算法的升级版 更难懂了

本篇Blog先介绍第二种

前置知识

1.并查集

2.知道树是啥玩意

3.贪心(刚讲了肯定会)

正题

Kruskal算法实际上非常简单,我们先来一道例题结合着讲解

P3366 【模板】最小生成树

很显然,为了求最小生成树,我们先需要存入所有的数据,为了方便,我们使用万能的 结构体 来存数据

struct node
{
	int x,y,t;//x表示一个点,y表示另一个点,t表示连接这条边需要的价值(也就是权值)
}k[200005];

然后我们输入数据

for(register int i=1; i<=m;i++)
	{
		scanf("%d%d%d",&k[i].x,&k[i].y,&k[i].t);
	}

最小生成树,肯定得有个求“最小”的过程,这里我们利用贪心的思想,把权值最小的连边放在数组的前面

inline bool cmp(node a,node b)
{	
	return a.t<b.t;//从小到大排序
}



	sort(k+1,k+m+1,cmp);

然后前提准备好了,我们进入程序的主题,即“建树”的环节,这里我们用并查集的思想


inline int find(int x)//路径压缩并查集查找,这里不再细说
{
	if(f[x]==x) return f[x];
	return f[x]=find(f[x]);
}


int now=0;//现在所建边的权值总数
	for(register int i=1; i<=m;i++)
	{
		if(n==1)//这里需要理解一下,当联通块的数量降为1的时候,我们就已经建好了树,直接输出退出即可(自己画个树试试,边数一定是节点数-1)
		{
			cout<<now;
			return 0;
		}
		int f1=find(k[i].x),f2=find(k[i].y);//看这俩节点是否连通
		if(f1!=f2) //如果不连通
		{
			f[f1]=f[f2];//让他俩连通
			now+=k[i].t;//权值++
			n--;//连通块--
		}
		else continue;
	}
	cout<<"orz";//如果没有要输出orz

然后这题就AC力

完整AC代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[200005];
inline int find(int x)
{
	if(f[x]==x) return f[x];
	return f[x]=find(f[x]);
}
struct node
{
	int x,y,t;
}k[200005];
inline bool cmp(node a,node b)
{
	return a.t<b.t;
}
int main()
{
	cin>>n>>m;
	for(register int i=1; i<=n;i++) f[i]=i;
	for(register int i=1; i<=m;i++)
	{
		scanf("%d%d%d",&k[i].x,&k[i].y,&k[i].t);
	}
	sort(k+1,k+m+1,cmp);
	int now=0;
	for(register int i=1; i<=m;i++)
	{
		if(n==1)
		{
			cout<<now;
			return 0;
		}
		int f1=find(k[i].x),f2=find(k[i].y);
		if(f1!=f2) 
		{
			f[f1]=f[f2];
			now+=k[i].t;
			n--;
		}
		else continue;
	}
	cout<<"orz";
	return 0;
}

然后附一张Kruskal算法的示意图,方便理解

posted @ 2021-06-26 19:04  Edolon  阅读(54)  评论(0编辑  收藏  举报
Live2D