浅谈最小生成树

部分内容摘自 李煜东《算法进阶》(个人认为非常好的一本书)

一些有关最小生成树的知识

个人比较蒻,讲得可能不太清楚,建议大家还是翻翻书,查查资料,找一些比较权威的说法。那么接下来就是蒟蒻自己的一些理解了

最小生成树,就是给你一个无向图,让你选一些边出来,然后让这些边形成的新的图保持连通且边权之和最小。这样来理解可能比较通俗易懂,其实这么看来,让一个图保持连通且权值最小,先不考虑权值,我们最少需要n-1条边让这个图保持连通,此时这就是一个树形结构(可能大概这就是为什么叫最小生成树的原因吧),但是注意最小生成树不能直接跑最短路

那么如何构造出这一颗最小生成树呢?涉及到两种算法,一个是Prim算法和Kruskal算法

Kruskal算法

时间复杂度为O(m log m)

Kruskal算法总是会去维护无向图中的最小生成森林。最初,森林由零条边构成,每个节点自己构成一棵树

在生成最小生成树的过程中,会在剩余的边中选一条边权最小的边加入到森林中,且保证这条边连接的两个点不属于同一集合(还没有被放在森林中)。这样的话,我们就可以用并查集来实现这一个过程,不会的并查集的可以去看一下我另一个博客(无耻宣传

Kruskal算法详细流程:
1.并查集初始化,每一个节点自己构成一个集合
2.将所有边按边权从小到大排序,遍历所有边(x,y,z)
3.若x,y不属于同一集合,那么加入这一条边,并将x,y合并;否则continue
4.所有的边扫描完之后,最小生成树已经构成 
struct edge{
	int x,y,z;
}e[MAXN];
bool cmp(edge q,edge w){
	return q.z

Prim算法

时间复杂度为O(n^2),堆优化之后为O(m log n),但其实堆优化之后不如Kruskal方便,所以Prim在处理稠密图和完全图时更加优秀,但是Kruskal还是比较实用的

Prim算法总是维护最小生成树的一部分,最初,Prim算法只确定1号属于最小生成树。我们设已经确定属于最小生成树的节点集合为T,剩余的节点集合为S。我们找到两个端点x和y,分别属于T和S,且边权最小,将y从S中删去,并加入到集合T中

我们可以维护一个数组d:对于x ∈ S,d[x]表示节点x与集合T中的节点之间权值最小的边的权值。若x ∈ T,则d[x]就等于x被加入T时选出的最小边的权值

int a[MAXN][MAXN],d[MAXN],n,m,ans;
bool v[MAXN];

void prim(){
	memset(d,0x3f,sizeof d);
	memset(v,0,sizeof v);
	d[1]=0; //将1加入进去 
	for(register int i=1;i

以上大概就是对两种算法的讲解了,\(Prim\)讲解得不太清楚,可能需要读者自己理解一下(是我太菜。。。)

P2230 繁忙的都市

模板题,自己做,完全没有思维难度

P2504 聪明的猴子

我们之后的省选是这种难度那多好。

这道题稍微那么不模板一点,为什么呢?因为它有x轴和y轴,所以我们的边数应该是\(n^2\),所以加一些处理就好了,还是把代码放出来

#include 
using namespace std;
double maxn=-20050206.0;
int n,m,ans,tot,now,x[2000010],y[2000010],a[2000010],fa[2000010],flag[2000010];

struct node {
	int x,y;
	double v;
} e[2000010];

inline bool cmp(node xx,node yy) {
	return xx.v=maxn) ans++;
	}
	printf("%d",ans);
	return 0;
}

P2212 Watering the Fields S

上面一道题的一点点变种,只是对于不符合条件的边直接丢旁边不管就行了

#include
using namespace std;
const int MAXN=2e7+50;
int n,c;
int x[MAXN],y[MAXN];
struct node {
	int x,y;
	int z;
} e[MAXN];
bool cmp(node q,node w) {
	return q.z

P1194 买礼物

这个题很舒服啊,它是一个矩阵,所以它是一个对称的图形,我们的读入只需要读取其中的一半,而剩下的一半再读入就会出现重边的情况。再看这道题的话,记得比较优惠和原价谁更便宜(为什么优惠之后会更贵啊)

#include 
using namespace std;
int n,m,u,tot,now,ans,fa[500010],flag[500010];

struct node {
	int x,y,v;
} e[500010];

inline bool cmp(node xx,node yy) {
	return xx.vi) {
				e[++tot].x=i;
				e[tot].y=j;
				e[tot].v=u;
			}
		}
	}
	sort(e+1,e+1+tot,cmp);
	for(register int i=1;i<=tot;i++) {
		if(now==m-1) break;
		int b=find(e[i].x);
		int c=find(e[i].y);
		if(b!=c) {	
			fa[b]=c;
			now++;
			ans+=min(e[i].v,n);
		}
	}
	printf("%d",ans+n);
	return 0;
}

你可能感兴趣的:(浅谈最小生成树)