最小生成树

 INTRODUCTION

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

--百度百科

概述

在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此的权重,若存在 T 为 E 的子集(即)且为无循环图,使得

的 w(T) 最小,则此 T 为 G 的最小生成树

最小生成树其实是最小权重生成树的简称。

--百度百科

最小生成树的性质:

最小生成树性质:设G=(V,E)是一个连通网络,U是顶点集V的一个非空真子集。若(u,v)是G中一条“一个端点在U中(例如:u∈U),另一个端点不在U中的边(例如:v∈V-U),且(u,v)具有最小权值,则一定存在G的一棵最小生成树包括此边(u,v)。--百度百科

通俗来说

对于一个连通图,若已经求出最小生成树A,则若能找出最小生成树B与A不同

则这个图中,至少有两条边边权相等,且两条边在同一个环上,删去这两条边中的任意一条不影响原图的连通性,

且在生成树A,B中等边权的边的数量相等

 推论

对于一个连通图,将他制成一颗生成树,使他的最大边最小,则这棵树是该图的最小生成树

不存在比最小生成树中最大边i更小的边j,当j将i替换后原图联通,若替换后仍联通,则j的权值必定比i大

因为在求最小生成树时已经保证最小联通

最小生成树的求法:

稀疏图Kruskal快,稠密图Prim快,根据题意选择使用的算法

当然你也可以选择两个算法都写,在输入时判断一下,如果是稠密图就跑Prim不然的话就跑Kruskal

1,Kruskal

Kruskal 的时间复杂度为 O(mlogm)

基本思路

I 设最小生成树为 T = (V,ET),设置边集 ET 的初始状态为空集。 I 将 E 中的边按权值从小到大排序;从小到大开始依次选取边。 I 若选取的边使生成树 T 不形成回路(即仍然是一棵树或森林),则把 它加入 ET 中,保留作为 T 的一条边;否则将其舍弃。 I 如此进行下去,直到 ET 中包含 n−1 条边为止。最后的 T 即为最小 生成树

 

大致就是说将每一条边按照边权从小到大排序,之后按这个顺序扫一遍,若这条边的起点和终点尚未联通或间接联通,那加上这条边,把这两个点连起来,那么怎么判断联通呢?自然要靠并查集了

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 int f[100086];
 7 struct edge
 8 {
 9     int from;
10     int to;
11     int weight;
12 };
13 int n, m;
14 int tot;
15 long long ans;
16 vector<edge>e;
17 int find(int x)//普通的并查集
18 {
19     if (f[x] == x)
20         return x;
21     else
22         return f[x] = find(f[x]);
23 }
24 void merge(int a, int b)
25 {
26     int x = find(a);
27     int y = find(b);
28     if (f[x] != f[y])
29         f[y] = x;
30 }
31 bool cmp(edge a, edge b)
32 {
33     return a.weight < b.weight;
34 }
35 int main()
36 {
37     cin >> n >> m;
38     for (int i = 1; i <= n; i++)
39         f[i] = i;//并查集初始化
40     for (int i = 1; i <= m; i++)
41     {
42         int x, y, z;
43         cin >> x >> y >> z;
44         edge t;
45         t.from = x;
46         t.to = y;
47         t.weight = z;
48         e.push_back(t);
49         t.from = y;
50         t.to = x;
51         e.push_back(t);
52     }
53     sort(e.begin(), e.end(), cmp);
54     for (int i = 0; i < e.size(); i++)
55     {
56         if (find(e[i].from) != find(e[i].to))//若你们俩没连在一起,那就连一下
57         {
58             merge(e[i].from, e[i].to);//合并并查集
59             ans += e[i].weight;
60         }
61     }
62     cout << ans << endl;
63     return 0;
64 }

2,Prim

Prim 的时间复杂度为 O(n2)

基本思路

设置一个顶点的集合 S 和一个边的集合 ET,S 和 ET 的初始状态均 为空集。 I 选定图中的任意一个顶点 K,从 K 开始生成最小生成树:将 K 加入 到集合 S; I 重复下列操作,直到选取了 n−1 条边:选取一条权值最小的边 x−y, 其中 x∈S, y̸∈S;将顶点 y 加入集合 S,边 x−y 加入集合 ET。 I 得到最小生成树 T = (S,ET)。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 struct edge
 5 {
 6     int from;
 7     int to;
 8     int weight;
 9     int next;
10 }e[500005];
11 const int inf = 20041001;
12 int head[100086];
13 int dis[100086];
14 int vis[100086];
15 int tot;
16 long long ans;
17 int n, m;
18 void add(int x, int y, int z)
19 {
20     tot++;
21     e[tot].to = y;
22     e[tot].from = x;
23     e[tot].next = head[x];
24     e[tot].weight = z;
25     head[x] = tot;
26 }
27 long long prim()
28 {
29     for (int i = 2; i <= n; i++)
30         dis[i] = inf;
31     for (int i = head[1]; i; i = e[i].next)
32         dis[e[i].to] = min(dis[e[i].to], e[i].weight);
33     int now = 1;
34     for (int t = 1; t < n; t++)
35     {
36         int minn = inf;
37         vis[now] = 1;
38         for (int i = 1; i <= n; i++)
39         {
40             if (!vis[i] && minn > dis[i])
41             {
42                 minn = dis[i];
43                 now = i;
44             }
45         }
46         ans += minn;
47         for (int i = head[now]; i; i = e[i].next)
48         {
49             int to = e[i].to;
50             if (dis[to] > e[i].weight && !vis[to])
51                 dis[to] = e[i].weight;
52         }
53     }
54     return ans;
55 }
56 int main()
57 {
58     cin >> n >> m;
59     for (int i = 1; i <= m; i++)
60     {
61         int x, y, z;
62         cin >> x >> y >> z;
63         add(x, y, z);
64         add(y, x, z);
65     }
66     cout << prim() << endl;
67     return 0;
68 }

 

posted @ 2019-07-31 13:26  STEllIAF0X  阅读(475)  评论(1编辑  收藏  举报