\(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;
}