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

    求最小生成树算法——Prim


    例题:

    https://www.luogu.org/problem/P3366

    算法:

    时间复杂度 : O(n²)

    • 算法主体思想:

    prim算法主要是用到贪心的思想,假设我们有两个集合A和B,A集合表示最小生成树集合(及A集合中的点都在最小生成树中),B集合表示非最小生成树集合(及B集合中的点都不在最小生成树中)。一开始,我们可以随便将一个点放入集合A,然后我们选择到最小生成树中距离最小的点放入A集合(前提是最小 && 当前点在B集合),然后用当前点更新其他在B集合中的点到A集合的距离。以此类推,循环n次之后(一共有n个点,所以n次之后,B集合为空,所有点都在A集合),就可得到最小生成树。

    • 算法主要变量声明:

    我们可以记录每个点到最小生成树集合中的最小距离,记为dis[i]————第i个点到最小生成树集合中的最小距离(注意:是到最小生成树集合的最短距离,不是到起点)。然后我们还需知道一个点是否在最小生成树集合中,于是我们用vis[i]表示i是否在最小生成树集合中(在A集合,还是在B集合)。当然我们可以用f[i][j]来记录第i个点到第j个点的距离。

    • 算法主要步骤:

    1. 初始化所有点都在B集合中,所有点到A集合中的距离都为0

    2. 随机选择一个点(选1即可),并将当前点到最小生成树的距离为0

    3. 重复n次以下步骤(及把所有点都放到A集合)

    1. 定义变量minn表示每次搜到的到最小生成树集合的最小距离(初始化为INF), k表示搜到的最小值的编号
    2. 搜索B集合中到A集合的最小值
    3. 标记搜到的点在A集合
    4. ans来累加当前点到最小生成树集合的距离
    5. 通过当前搜到的点k,来更新其他点到最小生成树集合的最短距离

    4. 得到最小生成树,边权之和在ans里


    示意图:


    code:

     1 #include <bits/stdc++.h>
     2 #define INF 0x3f3f3f3f//定义最大值(0x3f3f3f3f是一个很大的数) 
     3 using namespace std;
     4 int n, m, dis[1001], vis[1001], f[1001][1001], ans;//dis[i]表示第i个点到最小生成树集合的最小值(及到最小生成树中任意点的最小值), vis[i]表示第i个点是否在最小生成树中, f[i][j]表示从第i个点到达第j个点的最小值(无法到达就赋INF), ans记录最小生成树边权 
     5 inline void prim(int s)//Prim模板 
     6 {
     7     memset(vis, 0, sizeof(vis));//初始化为未在最小生成树中 
     8     memset(dis, INF, sizeof(dis));//初始化为最大值 
     9     dis[s] = 0;//初始点s到最小生成树生成树中距离为0 
    10     for(register int i = 1; i <= n; ++i)//n个点都要连通就要把n个点都放进最小生成树里 
    11     {
    12         int minn = INF;//用来记录每次搜到的离最小生成树集合的最小值 
    13         int k = 0;//用来记录搜到最小值的编号 
    14         for(register int j = 1; j <= n; ++j)//寻找最小值 
    15         {
    16             if(!vis[j] && dis[j] < minn)//不能访问过 && 离最小生成树的距离最短 
    17             {
    18                 minn = dis[j];//替换 
    19                 k = j;
    20             }
    21         }
    22         if(!k)//没有点在最小生成树中了(这个也可以判断图是否连通,如果k没有值,就代表图没有连通,因为最小生成树中点的数量一定是n(生成树的定义就是用n - 1条边,使得n个点能互相到达)) 
    23         {
    24             break;
    25         }
    26         vis[k] = 1;//标记 
    27         ans += dis[k];//累加最小值 
    28         for(register int j = 1; j <= n; ++j)
    29         {
    30             if(!vis[j] && dis[j] > f[k][j])//更新长度(这里是到最小生成树集合的最短长度,不是到s的最短长度) 
    31             {
    32                 dis[j] = f[k][j];//更新 
    33             }
    34         }
    35     }
    36     return;
    37 }
    38 signed main()
    39 {
    40     memset(f, INF, sizeof(f));//赋最大值 
    41     scanf("%d %d", &n, &m);
    42     for(register int i = 1, x, y, z; i <= m; ++i)
    43     {
    44         scanf("%d %d %d", &x, &y, &z);
    45         f[x][y] = f[y][x] = min(f[x][y], z)/*小心毒瘤数据*/;//连双向边 
    46     }
    47     prim(1);//从1开始就好了 
    48     printf("%d", ans);
    49     return 0;
    50 }
  • 相关阅读:
    商户编号前三位代码对应的收单机构大全
    使用邮件模板(freemarker.jar)发送邮件
    Struts2自定义拦截器
    如何在jsp页面显示存储在数据库的图片
    正则表达式 大于0的数字(包含小数)
    Linux的关机命令
    maven的下载和安装
    web.py的安装
    安装第三方插件BeautifulSoup
    myeclipse离线安装pydev插件
  • 原文地址:https://www.cnblogs.com/qqq1112/p/11217527.html
Copyright © 2011-2022 走看看