假设 nn 表示图中点数,mm 表示图中边数。

Prim算法堆优化

时间复杂度 O(nlogn)O(nlogn)

核心思想:每次挑一条与当前集合相连的最短边。

code

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
int ans,cnt;
struct edge{int v,w;};
vector<edge> e[N];
int d[N], vis[N];
priority_queue<pair<int,int>> q;

bool prim(int s){
for(int i=0;i<=n;i++) d[i]=inf;
d[s]=0; q.push({0,s});
while(q.size()){
int u=q.top().second; q.pop();
if(vis[u])continue;//再出队跳过
vis[u]=1;//标记u已出队
ans+=d[u]; cnt++;
for(auto ed : e[u]){
int v=ed.v, w=ed.w;
if(d[v]>w){
d[v]=w;
q.push({-d[v],v});//大根堆
}
}
}
return cnt==n;
}

Kruskal算法

适用于稀疏图,时间复杂度 O(mlogm)O(mlogm)

核心思想:从小到大挑不多余的边。

C++ 代码

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
67
68
69
70
71
72
73
74
#include <bits/stdc++.h>
using namespace std;

const int N=200006;
int n, m;
int ans=0;
struct edge{
int u,v,w;
bool operator<(const edge &t)const
{return w < t.w;}
}e[N];//结构体存边
struct DSU {
vector<int> f, siz;

DSU() {}
DSU(int n) {
init(n);
}

void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}

int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}

bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}

int size(int x) {
return siz[find(x)];
}
};
bool kruskal(){//返回的是原图的连通性,最小生成树的答案并没有返回,注意自己处理。
sort(e+1,e+m+1);
DSU dsu(n+1);
int cnt=0;
for(int i=1; i<=m; i++){
int x=(e[i].u);
int y=(e[i].v);
if(!dsu.same(x,y))
{
dsu.merge(x,y);
ans+=e[i].w;
cnt++;
}
}
return cnt==n-1;
}
int main(){
cin>>n>>m;
for(int i=1; i<=m; i++)
cin>>e[i].u>>e[i].v>>e[i].w;
if(!kruskal()) puts("impossible");
else printf("%d\n",ans);
return 0;
}