zoukankan      html  css  js  c++  java
  • 最小生成树

     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 }
  • 相关阅读:
    H
    GIS+=地理信息+行业+大数据——基于云环境流处理平台下的实时交通创新型app
    计算机图形学(一) 视频显示设备_3_随机扫描显示器
    实战c++中的vector系列--构造、operator=和assign差别
    【技术】正則表達式—匹配电话号码,网址链接,Email地址
    LeetCode(38)-Valid Sudoku
    传智播客《巴巴运动网视频教程(11-106)》avi格式以及兴许44集视频包括所有源码和资源
    mongodb常见管理命令
    Java web 项目读取src或者tomcat下class文件夹下的xml文件或者properties文件
    Axure 8.0.0.3312下载地址以及注册码
  • 原文地址:https://www.cnblogs.com/HNFOX/p/11274539.html
Copyright © 2011-2022 走看看