0%

最小生成树

最小生成树

alt

本文时间复杂度中: $n$ 为节点数, $m$ 为边数。

朴素版Prim $O(n^2)$

思路:

类似于 $Dijkstra$,从任意一点开始,先更新该点的dist,再以该点为中继,更新其余点到已确定距离的集合的距离;之后的循环都是找到离当前已经确定了距离的集合最近的点,并以重复下一次循环。

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
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510, M = 1e5 + 10;
int myg[N][N], dist[N];
int n, m;
bool st[N];

int prim()
{
int res = 0;//记录最小生成树的路径长度
memset(dist, 0x3f, sizeof(dist));
memset(st, 0, sizeof(st));
for (int i = 0; i < n; ++i)//从0开始比较方便,1开始也不是不可以,注意要循环n次,相当于每个节点一条边,初始点边权为0
{
int t = -1;
for (int j = 1; j <= n; ++j)
if (!st[j] && (t == -1 || dist[t] > dist[j]))//类似dijkstra
t = j;
if (i && dist[t] == 0x3f3f3f3f)
return 0x3f3f3f3f;
if (i)//i为0时为第一个点,到集合的距离为零,因此不用运算
res += dist[t];
for (int j = 1; j <= n; ++j)
dist[j] = min(dist[j], myg[t][j]);//更新区别于dijkstra,更新的是到集合的距离,而且可以看出被更新的只会是与t相连的
st[t] = true;
}
return res;
}

int main()
{
scanf("%d%d", &n, &m);
memset(myg, 0x3f, sizeof(myg));
while (m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
myg[u][v] = myg[v][u] = min(myg[u][v], w);
}
int res = prim();
if (res == 0x3f3f3f3f)
puts("impossible");
else
printf("%d", res);
return 0;
}

Kruskal $O(mlogm)$

思路:

使用了并查集。首先排序,按边权从小到大排序,再用并查集,即如果不属于同一集合,则加入集合。而且该算法没有必要用临界表或邻接矩阵来存,直接用结构体存起终点和权值就可以。

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
#include <cstdio>
#include <algorithm>
using namespace std;

int n, m;
const int N = 1e5 + 10,M = 2e5 + 10;
int p[N];
struct edge
{
int l, r, w;
bool operator < (const edge &W)
{
return w < W.w;
}
}e[M];

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

int main()
{
scanf("%d%d", &n, &m);
int u, v, w;
for(int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &u, &v, &w);
e[i] = {u, v, w};
}
sort(e + 1, e + m + 1);
for(int i = 1; i <= n; ++i)
p[i] = i;
int res = 0, cnt = 0, l, r;
for(int i = 1; i <= m; ++i)
{
l = e[i].l, r = e[i].r, w = e[i].w;
l = find(l), r = find(r);
if(l != r)
res += w, p[l] = r, ++cnt;
}
if(cnt < n - 1)
puts("impossible");
else
printf("%d", res);
return 0;
}