最小生成树之Kruskal

模板题,学习一下最小生成树的Kruskal算法

 

对于一个连通网(连通带权图,假定每条边上的权均为大于零的实数)来说,每棵树的权(即树中所有边的权值总和)也可能不同

具有权最小的生成树称为最小生成树

生成树:

  • 无向连通图的边的集合
  • 无回路
  • 连接所有的点

最小:

  • 所有边的权值之和最小

n个顶点的树有n-1条边

时间复杂度:O(ElogE)

 

对于稀疏图来说

按所给的边的权值从小到大排序,如果该边不与已经选的边形成环就选择它

这里用并查集来实现

第i条边的端点放在u、v数组中,权值保存在w中

这里用的是间接排序,也就是排的是每条边的序号,放在rank数组中

 

下面是两道模板题:

HDU 1863 畅通工程

 1 //#define LOCAL

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 using namespace std;

 6 

 7 const int maxn = 5000;

 8 int u[maxn], v[maxn], w[maxn], parent[maxn], rank[maxn];

 9 int m, n;

10 

11 bool cmp(const int i, const int j)

12 {

13     return (w[i] < w[j]);

14 }

15 

16 int GetParent(int a)

17 {

18     return parent[a] == a ? a : parent[a] = GetParent(parent[a]);

19 }

20 

21 int kruskal(void)

22 {

23     int cnt = 0, weight = 0;

24     for(int i = 0; i < m; ++i)

25     {

26         int edge = rank[i];

27         int x = GetParent(u[edge]);

28         int y = GetParent(v[edge]);

29         if(x != y)

30         {

31             weight += w[edge];

32             ++cnt;

33             parent[x] = y;

34         }

35     }

36     if(cnt < n - 1)    weight = 0;

37     return weight;

38 }

39 

40 int main(void)

41 {

42     #ifdef LOCAL

43         freopen("1863in.txt", "r", stdin);

44     #endif

45 

46 

47     while(scanf("%d%d", &m, &n) == 2 && m)

48     {

49         for(int i = 0; i < m; ++i)

50             scanf("%d%d%d", &u[i], &v[i], &w[i]);

51         for(int i = 0; i < n; ++i)    parent[i] = i;

52         for(int i = 0; i < m; ++i)    rank[i] = i;

53         sort(rank, rank + m, cmp);

54         int ans = kruskal();

55         if(ans)

56             printf("%d\n", ans);

57         else

58             printf("?\n");

59     }

60     return 0;

61 }
代码君一

 

POJ 1861 Network

感觉这道题略坑啊,它并没有说是多组输入啊,而且输出的第一个数是边里面的最大权值啊,数组开了1000多开小了啊,还有各种小错误啊。Orz

好吧,这些都是我的错误,上来就套模板,没有好好读题

 1 //#define LOCAL

 2 #include <iostream>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <algorithm>

 6 using namespace std;

 7 

 8 const int maxn = 1000 + 10;

 9 int v[maxn], u[maxn], r[maxn], p[maxn], w[maxn], path[maxn];

10 int n, m, cnt, ans;

11 

12 int Find(int a)

13 {

14     return p[a] == a ? a : p[a] = Find(p[a]);

15 }

16 

17 bool cmp(const int i, const int j)

18 {

19     return (w[i] < w[j]);

20 }

21 

22 void Kruskal(void)

23 {

24     cnt = 0, ans = -1;

25     for(int i = 0; i < m; ++i)

26     {

27         int edge = r[i];

28         int x = Find(u[edge]);

29         int y = Find(v[edge]);

30         if(x != y)

31         {

32             ans = max(ans, w[edge]);

33             p[x] = y;

34             path[cnt++] = i;

35         }

36     }

37 }

38 

39 void OutPut(void)

40 {

41     printf("%d\n%d\n", ans, cnt);

42     for(int i = 0; i < cnt; ++i)

43         printf("%d %d\n", u[r[path[i]]], v[r[path[i]]]);

44 }

45 

46 int main(void)

47 {

48     #ifdef LOCAL

49         freopen("1861in.txt", "r", stdin);

50     #endif

51 

52     while(scanf("%d%d", &n, &m) == 2)

53     {

54         for(int i = 0; i < m; ++i)

55         scanf("%d%d%d", &u[i], &v[i], &w[i]);

56         for(int i = 0; i < n; ++i)    p[i] = i;

57         for(int i = 0; i < m; ++i)    r[i] = i;

58         sort(r, r + m, cmp);

59         Kruskal();

60         OutPut(); 

61     }

62     

63     return 0;

64 }
代码君二

 

POJ 2560 Freckles

题意:给出n个点的坐标,求最小生成树的长度。奇怪的是G++没过,C++却过了

 1 //#define LOCAL

 2 #include <iostream>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <algorithm>

 6 #include <cmath>

 7 using namespace std;

 8 

 9 const int maxn = 5000 + 10;

10 struct Node

11 {

12     double x, y;

13 }pos[maxn];

14 

15 int u[maxn], v[maxn], r[maxn], p[maxn];

16 double w[maxn];

17 

18 bool cmp(const int i, const int j)

19 {

20     return (w[i] < w[j]);

21 }

22 

23 int Find(int a)

24 {

25     return p[a] == a ? a : p[a] = Find(p[a]);

26 }

27 

28 double Kruskal(int cnt)

29 {

30     double ans = 0.0;

31     for(int i = 0; i < cnt; ++i)

32     {

33         int edge = r[i];

34         int x = Find(u[edge]);

35         int y = Find(v[edge]);

36         if(x != y)

37         {

38             ans += w[edge];

39             p[x] = y;

40         }

41     }

42     return ans;

43 }

44 

45 int main(void)

46 {

47     #ifdef LOCAL

48         freopen("2560in.txt", "r", stdin);

49     #endif

50 

51     int n, cnt;

52     while(scanf("%d", &n) == 1)

53     {

54         for(int i = 0; i < n; ++i)    p[i] = i;

55         for(int i = 0; i < n; ++i)

56             scanf("%lf %lf", &pos[i].x, &pos[i].y);

57         cnt = 0;

58         for(int i = 1; i < n; ++i)

59             for(int j = 0; j < i; ++j)

60             {

61                 u[cnt] = i;

62                 v[cnt] = j;

63                 r[cnt] = cnt;

64                 w[cnt++] = sqrt((pos[i].x-pos[j].x)*(pos[i].x-pos[j].x) + (pos[i].y-pos[j].y)*(pos[i].y-pos[j].y));

65             }

66         sort(r, r + cnt, cmp);

67         printf("%.2lf\n", Kruskal(cnt));

68     }

69     

70     return 0;

71 }
代码君三

 

你可能感兴趣的:(最小生成树)