洛谷 P2126 【Mzc家中的男家丁】

从题目不难看出这是一道最小生成树的裸题,然后再看数据范围:

$n <= 2300 $
\(m <= 400000\)

首先想到Kruskal或者Prim都可以满足需要,但考虑到这两种算法中Kruskal应用更广,所以今天我们重点讲一下Kruskal算法

首先回归最小生成树的定义:给定一张边带权的无向图\(G = (V, E)\),\(n=|V|\),由\(V\)\(n\)个节点和\(E\)\(n-1\)条边构成的无向连通图称为\(G\)的一棵生成树。边的权值之和最小的生成树被称为最小生成树。

进入今天的正题:Kruskal算法

Kruskal算法是基于一种贪心的思想,他总是维护无向图的最小生成森林。最初,生成森林是由零条边构成,每个节点构成一棵仅包含一个节点的树。

Kruskal算法的主体步骤,就是不断从剩余的边中选出一条权值最小,且他的两个端点在生成森林中分属于两棵不同的树,将这条边加入生成森林中。

那么,我们自然就会想到图的连通性应该如何维护呢? 为了代码效率和简便,我们一般都采用并查集来维护。(事实上,Kruskal算法的巨大优越性也是在加入并查集优化后才得以体现的,而我们平时所说的Kruskal一般都是带并查集优化的。)

算法流程:

1.建立并查集,每个点构成一个集合。

2.将各边按权值从小到大排序,依次扫描每条边。

3.若一条边两端点在同一集合中,则忽略这条边直接跳过。

4.否则,合并他们所在的集合,将权值之和累加到答案中。

5.第四步中选中的边就构成了这张图的最小生成树。

一图胜千言:

这是一幅无向图:

将各边按权值从小到大排完序后应为:

x      y       val
1      2        2
1      3        2
1      4        3
3      4        3
2      3        4

然后我们开始执行Kruskal的主体流程:

枚举第一条边(1, 2, 2),将其加入生成森林中,生成森林变为:

枚举第二条边(1, 3, 2),将其加入生成森林中,生成森林变为:

枚举第三条边(3,4,3),将其加入生成森林中,生成森林变为:

你会发现,此时我们已经选出\(n - 1\)条边,那么这就是这张图的最小生成树,将权值累加起来即为答案,Kruskal算法结束。

Ps:这幅图比较简单,但是重在理解Kruskal的流程。

原理理解之后,代码就会变得异常简单了,注意,重点在于一定要理解,废话不多说,直接上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 400005

using namespace std;

int n, m, fa[maxn], tot, head[maxn], sum;

struct Edge{
	int x, y, w;
}edge[maxn];

bool cmp(Edge x,Edge y){return x.w < y.w;} //自定义比较函数

int find(int x){reutnr x == fa[x] ? x : fa[x] = find(fa[x]);}

bool judge(int x,int y){return find(x) == find(y);}

void merge(int x,int y){fa[find(x)]=find(y);}
//优雅的三行并查集
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++){
		cin >> edge[i].x >> edge[i].y >> edge[i].w;
		edge[i].x++, edge[i].y++;
	}
	sort(edge+1, edge+m+1, cmp); //权值从大到小排序
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++){
		int x = edge[i].x, y = edge[i].y;
		if (!judge(x,y)){
            sum += edge[i].w;
			merge(x,y);
		}
	}                           //当然,这部分也可以封装到函数中,看个人喜好
	cout << sum << '\n';
	return 0;
}

最后,本篇题解旨在帮助那些刚刚接触到最小生成树的OIer们更好的理解,若有任何问题,请私信本人。

posted @ 2019-10-25 15:01  Hydrogen_Helium  阅读(106)  评论(0编辑  收藏  举报