什么是最小生成树?

  • 在一个有$n$个点,$m$条边的无向连通图中,已知各边的权值,现在我们要删去一些边,保留一些边,使得图仍然保持连通,求保留边的边权之和的最小值

Kruskal算法

算法原理

​ Kruskal算法的基本算法是贪心,即从小到大加入每一条边

前置知识

​ 快速排序,结构体存图,并查集

实现思路&贪心正确性

​ 利用贪心的思想,将所有边根据边权从小到大排序,然后如果当前这条边所连接的两个结点不连通,就把这条边加进来,这样就可以保证,在使两个结点保持联通的过程中所增加的边的边权是最小的,QED

实现技巧

​ 排序使用$sort$,判断结点是否联通以及加边用并查集实现

Code

模板题,洛谷P3366

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10,M=2e5+10;
int p[N];
struct edge{
int x,y,c;
}a[M];
int find(int x)
{
if(x!=p[x])
{
p[x]=find(p[x]);
}
return p[x];
}
void merge(int i,int j)
{
p[find(i)]=find(j);
}
bool cmp(edge q,edge p)
{
return q.c<p.c;
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
p[i]=i;
}
for(int i=1;i<=m;i++)
{
cin>>a[i].x>>a[i].y>>a[i].c;
merge(a[i].x,a[i].y);
}
for(int i=1;i<n;i++)
{
if(find(i)!=find(i+1))
{
cout<<"orz"<<endl;
return 0;
}
}
for(int i=1;i<=n;i++)
{
p[i]=i;
}
sort(a+1,a+m+1,cmp);
int ans=0,sum=0;
for(int i=1;i<=m;i++)
{
if(find(a[i].x)!=find(a[i].y))
{
merge(a[i].x,a[i].y);
sum++;
ans+=a[i].c;
}
if(sum==n-1)
{
break;
}
}
cout<<ans<<endl;
return 0;
}

简单练习题

P1195 口袋的天空

SCOI2005 繁忙的都市