最小生成树(模板)

题目

这是个模板题
链接:P3366 【模板】最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。

输入格式

第一行包含两个整数 $ N,M $ ,表示该图共有 $ N $ 个结点和 $ M $ 条无向边。
接下来 $ M $ 行每行包含三个整数 $ X_i,Y_i,Z_i $ ,示有一条长度为 $ Z_i $ 的无向边连接结点 $ X_i,Y_i $。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。

数据规模

对于 20% 的数据,$ N≤5,M≤20 $ 。

对于 40% 的数据,$ N≤50,M≤2500 $ 。

对于 70% 的数据,$ N≤500,M≤10 $ 。

对于 100%100% 的数据:$ 50001≤N≤5000, 1≤M≤2×10^5 $ 。

题解

Kruskal 算法的主要思路:

  1. 先以边的权值来给边排序,从小到大

  2. 然后开始由小到大遍历边,也就是一步一步的点的扩展

  3. 然后判断这个点的是否在已拓展的点内,如果不在就将答案加上它的边权,并且加入并查集

注意一下,这个是用邻接表edge写的(u,v为边的两个点,为边权),没有用领接矩阵

基本上这个模板就这样了,代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

struct edge{ 
	int u,v,w;    //定义一个边的结构体,u、v为边的两个定点,w为边的边权
};



edge e[200005];
int father[5005];      //并查集
int ans = 0;

bool cmp(const edge& a,const edge& b){
	return a.w < b.w;        //sort()的判断,以边权的由大到小的顺序来排序 
}

int found(int x){          //并查集的寻找父亲
	return father[x] == x ? x : father[x] = found(father[x]);        //压行,并且压缩路径,这样可以将之后的查找时间变为常数级
}

int main()
{
	int n,m;

	cin>>n>>m;

	for(int i = 1;i <= m;i++)
	{
		scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
	}

	sort(e,e+m,cmp);    //kruskal 算法的重要部分:按照边权的大小来排序

	for(int j = 1;j<=n;j++)	father[j] = j;  //初始化,每一个点都是一个自己的父亲

	int k = 0;
	for(int j = 1;j<=m;j++)
	{
		if(k == n - 1)	break;
		int v = e[j].v,u = e[j].u;
		if(found(v) != found(u))        //如果没有这个边所在的点没有加入,就执行下面
		{
			k++;   //注意一点:这一句话一定要在这里写,我已经掉过坑里了
			ans += e[j].w;    //加入答案:最小生成树的总长
			
			int f = found(v),t = found(u);
			father[t] = f;          //使这个的边的这个点加入路径
		}
	}




	int cnt = 0;
	for(int i = 1;i<=n;i++)
		if(father[i] == i)
                	cnt++;
	
	
	if(cnt == 1)
		cout<<ans<<endl;
	else
		cout<<"orz"<<endl;        //如果有边不联通,就输出“orz”
	getchar();getchar();
	return 0;
}
posted @ 2021-02-20 15:50  在那遥远的悠穹下  阅读(74)  评论(0编辑  收藏  举报