\(Kruskal\)

\(Kruskal\)算法将一个连通块当做一个集合。

\(Kruskal\)首先将所有的边按从小到大顺序排列(一般使用快排),并认为每一个点都是孤立的,分属于\(n\)个独立的集合。

然后按顺序从小到大枚举每一条边。如果这条边连接这两个不同集合,那么就把这条边加入最小生成树,这两个集合就合并成了一个集合;如归这条边连接的两个点属于同一集合,就跳过。

直到选取了\(n-1\)条边结束。

核心代码:

int kruskal(){
	int MST=0;
	init();
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++){
		if(!same(a[i].a,a[i].b)){
			unite(a[i].a,a[i].b);
			MST+=a[i].w;
		}
	}
	return MST;
} 

时间复杂度\(O(mlogm)\)



其中\(init(),same(),unite()\)都属于并查集自定义函数:

并查集模板:

int n,fa[MAXN];

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

int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}

void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return ;
	fa[y]=x;
}

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

例题一:

题目描述 Description

给你树的棵数N,再给你M个关系,表示哪些树可以连成一片。现在要你把所有树连成K片小树林,一片小树林最少要用掉一棵树,请问怎么连花费的代价最小。

输入描述 Input Description

第一行有三个数N,M,K \((1≤N≤1000,1≤M≤10000,1≤K≤10)\)
接下来M行每行三个数X,Y,L,表示X树和Y树可以通过L的代价连在一起。\((1≤X,Y≤N,0≤L<10000)\)

输出描述 Output Description

输出一个整数,表示最小的代价。
如果连不出K片树林,请输出“No Answer”。

样例输入 Sample Input

3 1 2
1 2 1

样例输出 Sample Output

1

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MAXN 1010
using namespace std;

inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}

int n,m,k,fa[MAXN];

struct tree{
	int a,b,w;
};

tree a[10010];

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

int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}

void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return ;
	fa[y]=x;
}

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

bool cmp(tree t1,tree t2){
	return t1.w<t2.w;
}

int kruskal(){
	init();
	int MST=0;
	sort(a+1,a+1+m,cmp);
	int cnt=0;
	for(int i=1;i<=m;i++){
		if(!same(a[i].a,a[i].b)){
			unite(a[i].a,a[i].b);
			MST+=a[i].w;
			cnt++;
		}
		if(cnt>=n-k){
			break;
		}
	}
	/*
	if(cnt<n-k){
		cout<<"No answer";
		return -1;
	}*/
	return MST;
}

int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		cin>>a[i].a>>a[i].b>>a[i].w;
	}
	int tmp=kruskal();
	if(tmp!=-1) cout<<tmp;
	return 0;
}

ICtiger's Blog 求关注 $e^{ix}=cosx+isinx$