最小生成树(学习笔记)

目的:为了让无向图所有的点联通,且长度各边长度之和最小。

关键:图中不应存在回路,图中只需要n-1条边,n为点的数量。

最小生成树的两种方法:Prim和Kruskal。


P3366 【模板】最小生成树https://www.luogu.com.cn/problem/P3366

题目描述

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

输入格式

第一行包含两个整数N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 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

说明/提示

数据规模:

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

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

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

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

样例解释:

所以最小生成树的总边权为 2+2+3=7。


kruskal就是排序加并查集。将所有的边排序,先选取较小的边,如果出现回路,则不选取。用并查集来判断是否出现回路。

Kruskal就像是往图中填边,只填n-1条边。

代码实现

#include<bits/stdc++.h>
using namespace std;

struct edge{
	int x;
	int y;
	int z;
}e[200000+5];

void init(int *parent,int n)
{
	for(int i=1;i<=n;i++)
	{
		parent[i]=i;
	}
}

int find(int *parent,int x)
{
	while(parent[x]!=x)
	{
		parent[x]=parent[parent[x]];
		x=parent[x];
	}
	return x;
}

int link(int x,int y,int *parent)
{
	int x_root=find(parent,x);
	int y_root=find(parent,y);
	if(x_root==y_root)
	{
		return 0;
	}
	else
	{
		parent[y_root]=x_root;
		return 1;
	}
}

void Quick_sort(int begin,int end)
{
	if(begin>=end)
	{
		return;
	}
	int i=begin;int j=end;
	while(i!=j)
	{
		while(e[j].z>=e[begin].z&&j>i)
		{
			j--;
		}
		while(e[i].z<=e[begin].z&&i<j)
		{
			i++;
		}
		edge temp=e[j];
		e[j]=e[i];
		e[i]=temp;
	}
	edge temp=e[begin];
	e[begin]=e[i];
	e[i]=temp;
	Quick_sort(begin,i-1);
	Quick_sort(i+1,end);
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
	}
	int parent[5000+5];
	init(parent,n);
	Quick_sort(1,m);
	int flag=0;
	long long ans=0;
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		flag=link(e[i].x,e[i].y,parent);
		if(flag) 
		{
			ans+=e[i].z;
			cnt++;
		}
	}
	if(cnt!=n-1)
	{
		printf("orz");
		return 0;
	}
	printf("%lld",ans);
    return 0;
}

prim稍后再学

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值