图论——解决最小生成树问题(Kruskal算法&Prim算法)

上周末的蓝桥杯省模拟赛时最后一题是一道最小生成树的题目,因为恰好在慕课上刚看到这个地方,所以现学了Prim算法,解决了这个题目(大概),赛后就打算多琢磨琢磨这一类题目。题目链接 最小生成树问题,是指给定无向图G=(V,E),连接G中所有点,且边集是E的子集的树称为G的生成树(Spanning Tree),而权值最小的生成树称为最小生成树(Minimal Spanning Tree,MST)。

一、Kruskal算法

Kruskal算法易于编写,且效率很高 紫书p356描述如下:

Kruskal算法的第一步是给所有边按照从小到大的顺序排列,这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每条边(u, v)。

  • 情况1:u和v在同一个连通分量中,那么加入(u, v)后会形成环,因此不能选择。

  • 情况2:如果u和v在不同的连通分量,那么加入(u, v)一定是最优的。 为什么呢?下面用反证法——如果不加这条边能得到一个最优解T,则T+(u, v)一定有且只有一个环,而且环中至少有一条边(u', v')的权值大于或等于(u, v)的权值。删除该边后,得到的新树T' = T + (u, v) - (u', v')不会比T更差。因此,加入(u, v)不会比不加入差。

下面是伪代码:

把所有边(按权值)排序,记第i小的边为e[i](1 <= i < m)
初始化MST为空
初始化连通分量,让每个点自成一个独立的连通分量
for(int i = 0; i < m; i++) {
	if(e[i].u 和 e[i].v 不在同一个连通分量) {
		把边e[i]加入MST
		合并e[i].u和e[i].v所在的连通分量
	}
}

在上面的伪代码中,最关键的地方在于 “连通分量的查询与合并” :需要知道任意两个点是否在同一连通分量中,还需要合并这两个连通分量。 有一种简介高效的算法可以用来处理这个问题——并查集 此处指路大佬解释:超有爱的并查集~ 将每个连通分量看作一个集合,该集合包含了连通分量中的所有点。这些点两两相通,而具体的连通方式无关紧要。在图中,每个点都恰好属于一个连通分量,对应到集合表示中,即为每个元素恰好属于一个集合。换句话说,图的所有连通分量可以用若干个不相交集合来表示。 奉上自己的并查集板子

#include <iostream>
using namespace std;
#define div 1000000007
typedef long long ll;
const int maxn = 1001;
int fa[maxn];//
inline void init(int n) {
    for (int i = 1; i <= n; i++)
        fa[i] = i;
}
int find(int x) {//查询+路径压缩 把沿途的每个节点的父节点都设为根节点
    return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
inline void merge(int i, int j) {
    fa[find(i)] = find(j);//前者的父节点设为后者
}

有了并查集之后,Kruskal算法的完整代码便不难给出了。假设第i条边的两个端点序号和权值分别保存在u[i],v[i]和 w[i]中,而排序后第i小的边的序号保存在r[i]中(间接排序,即排序的关键字是对象的“代号”,而不是对象本身),p为并查集

完整代码:

练习题:

题号 |题目名称(英文)|备注| |--|--|--|--| hdu1863| 畅通工程 | 最小生成树 板子题 hdu1879| 继续畅通工程 | 最小生成树 板子题 hdu1875|畅通工程再续|最小生成树 板子题 洛谷P3366| 【模板】最小生成树 |最小生成树 板子题

Problem Description 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。 Input 测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N 行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。 Output 对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。

这题相比上题来说,就将已经建好了的道路花费置为0即可,同时注意本题输入输出好像都不能用cincout,会超时。

Problem Description 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。 Input 测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。 当N为0时输入结束。 Output 每个测试用例的输出占一行,输出全省畅通需要的最低成本。

先输入所有顶点,再判断每两个顶点是否可以构成合适的边

Problem Description 相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。 Input 输入包括多组数据。输入首先包括一个整数T(T <= 200),代表有T组数据。 每组数据首先是一个整数C(C <= 100),代表小岛的个数,接下来是C组坐标,代表每个小岛的坐标,这些坐标都是 0 <= x, y <= 1000的整数。 Output 每组输入数据输出一行,代表建桥的最小花费,结果保留一位小数。如果无法实现工程以达到全部畅通,输出”oh!”.

板子题

二、Prim算法

来自陈越姥姥的数据结构 Prim算法基本思路——从一个根节点开始,让一棵小树长大。与Dijkstra算法相近。 Dijkstra算法伪代码:

Prim算法伪代码:

Prim算法完整代码

最后更新于

这有帮助吗?