当前位置:   article > 正文

最小生成树(Prim算法、Kruskal算法)_给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。

给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。

目录:

题目描述:

Kruskal算法:

代码及分析: 

Prim算法:

代码及分析:

样例:

题目描述:

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式:

第一行包含两个整数 N, M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​, Yi​, Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​, Yi​。

输出格式:

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

Kruskal算法:

将所有的边按权值的大小从小到大进行排序,选取权值最小的边,回贴到图中并判断是否形成了环,若形成了环,则丢弃该边,继续下一条边的回贴,若没有形成环,则递归调用,继续下条边的判断。此时的判断有没有形成环,可以用有没有公共祖先进行判断,若存在公共祖先,则会形成环,否则不会,详情见代码。

代码及分析: 

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = 5e5 + 10;
  4. const int maxv = 2e5 + 10;
  5. int n, m, ans, f[maxv];
  6. //因为该图为无向图,故存储时需要将数组长度扩大一倍
  7. struct edge{
  8. int u, v, w;
  9. }g[maxn];
  10. bool cmp(edge a,edge b) {
  11. return a.w < b.w;
  12. }
  13. int find(int a) {
  14. if (f[a] == a)
  15. return a;
  16. return f[a] = find(f[a]);
  17. }
  18. void construct() {
  19. int cnt = 0;
  20. for (int i = 1; i <= n; i++)
  21. f[i] = i;
  22. for (int i = 1; i <= m; i++) {
  23. int fx = find(g[i].u);
  24. int fy = find(g[i].v);
  25. //该步骤是为了判断是否形成了环
  26. if (fy != fx) {
  27. f[fx] = fy;
  28. ans += g[i].w;
  29. cnt++;
  30. }
  31. if (cnt == n - 1) {
  32. printf("%d", ans);
  33. break;
  34. }
  35. }
  36. if(cnt != n - 1)
  37. printf("orz");
  38. }
  39. int main() {
  40. scanf("%d %d", &n, &m);
  41. for (int i = 1; i <= m; i++)
  42. scanf("%d %d %d", &g[i].u, &g[i].v, &g[i].w);
  43. sort(g + 1, g + 1 + m, cmp);
  44. construct();
  45. return 0;
  46. }

Prim算法:

首先从某个点出发,找到与之相连的最小权值的边,并将其回贴到图中,然后在此时的最小生成树中,找到某点与(还未加入最小生成树中的某点)形成的最小权值的边,并将其回贴到图中,如此递归形成最后的结果,详情见代码。

代码及分析:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int INF = 1e9 + 10;
  4. const int maxn = 5010;
  5. const int maxm = 2e5 + 10;
  6. struct edge{
  7. int v, w, next;
  8. }e[maxm * 2];
  9. //cnt表示有向边的条数
  10. //tot表示已连接的最小生成树的边
  11. //now代表此时准备连接哪个点
  12. //ans表示结果
  13. int cnt, n, m, tot, now = 1, ans;
  14. //head表示某点作为前驱节点时的编号
  15. //dis表示已经加入最小生成树的点到没有加入的点的最小距离
  16. //vis判断某个点是否已经加入最小生成树
  17. int head[maxn], dis[maxn], vis[maxn];
  18. void add(int u, int v, int w) {
  19. e[++cnt].v = v;
  20. e[cnt].w = w;
  21. e[cnt].next = head[u];
  22. head[u] = cnt;
  23. }
  24. void prim() {
  25. for(int i = 2; i <= n; ++i)
  26. dis[i] = INF;
  27. //注意重边
  28. //找出所有与1相连的点,记录两点间的最短距离
  29. for(int i = head[1]; i; i = e[i].next)
  30. dis[e[i].v] = min(dis[e[i].v], e[i].w);
  31. //最小生成树边数等于点数 - 1
  32. while(++tot < n) {
  33. int minn = INF;
  34. //标记走过的点
  35. vis[now] = 1;
  36. int t = now;
  37. //枚举每一个没有使用的点
  38. //找出最小值作为新边
  39. for(int i = 1; i <= n; ++i)
  40. if(!vis[i] && minn > dis[i]) {
  41. minn = dis[i];
  42. now = i;
  43. }
  44. //如果最新的结点未被修改,则无法形成最小生成树
  45. //故该图不连通,则需输出orz
  46. if(now == t) {
  47. printf("orz");
  48. return;
  49. }
  50. ans += minn;
  51. //枚举now的所有连边,更新dis数组
  52. for(int i = head[now]; i; i = e[i].next) {
  53. int v = e[i].v;
  54. if(dis[v] > e[i].w && !vis[v])
  55. dis[v] = e[i].w;
  56. }
  57. }
  58. printf("%d", ans);
  59. }
  60. int main() {
  61. scanf("%d %d", &n, &m);
  62. int u, v, w;
  63. for(int i = 1; i <= m; ++i) {
  64. scanf("%d %d %d", &u, &v, &w);
  65. //该图为无向图
  66. //故需要当作两条边加入结构体中
  67. add(u, v, w);
  68. add(v, u, w);
  69. }
  70. prim();
  71. return 0;
  72. }

样例:

输入:

4 5

1 2 2

1 3 2

1 4 3

2 3 4

3 4 3

输出:

7

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/820844
推荐阅读
相关标签
  

闽ICP备14008679号