最小生成树(Prime和Kruskal)

在这里插入图片描述

最小生成树

n n n 个顶点,用 n − 1 n-1 n1 条边把一个连通图连接起来,并且使得权值的和最小的树就是最小生成树。
p r i m prim prim 算法采用的是一种贪心的策略。每次将离连通部分的最近的点和点对应的边加入的连通部分,连通部分逐渐扩大,最后将整个图连通起来,并且边长之和最小。
D i j k s t r a Dijkstra Dijkstra类似, P r i m Prim Prim 算法也可以用堆优化优先队列代替堆,优化的 P r i m Prim Prim 算法时间复杂度 O ( m l o g n ) O(mlogn) O(mlogn) ,适用于稀疏图。但是稀疏图的时候求最小生成树,Kruskal 算法更加实用

P r i m 算 法 求 最 小 生 成 树 Prim算法求最小生成树 Prim

P r i m Prim Prim算法和 d i j k s t r a dijkstra dijkstra 算法非常相似,Dijkstra算法是更新到起始点的距离,Prim是更新到集合S的距离
d i j k s t r a : dijkstra: dijkstra先初始化距离,每次找不在集合中,距离最小的点,来更新它的邻点到 1 1 1 号点的距离。这个集合是指已经确定了最短距离的点的集合
P r i m : Prim: Prim
1、先把所有距离初始化为正无穷
2、迭代 n n n 次: f o r ( i = 1 ; i < = n ; + + i ) for(i=1;i<=n;++i) for(i=1;i<=n;++i)
2.1、每次也是找集合外距离集合最短的点 t t t这里的集合是指已经在连通块中的点构成的集合
2.2、用 t t t 来更新其他点到集合的距离, d i j k s t r a dijkstra dijkstra是用 t t t 更新其他点到起点的距离
2.3、把 t t t加到集合中去,即 s t [ t ] = t r u e st[t]=true st[t]=true

模板题链接:AcWing 858. Prim算法求最小生成树
一些注意事项:
①:要把 a r r arr arr数组初始化为正无穷,存在重边
①:初始化dist数组的时候不用把设置 d i s t [ 1 ] = 0 dist[1]=0 dist[1]=0,此时的 d i s t dist dist 数组表示到集合的距离
②: i f ( i if(i if(i && d i s t [ t ] = = I N F ) dist[t] == INF) dist[t]==INF),把 i i i 放进去是因为第一个点的时候还没有生成树集合产生
③: i f ( i ) r e s + = d i s t [ t ] if(i) res += dist[t] if(i)res+=dist[t] ,此时的 r e s res res 表示第i次生成树中边权和的累加,当 i = 0 i=0 i=0 的时候只有一个点,不存在距离这一说法。这里要把这一句放在更新距离之前来做,是因为假如存在负自环,假如之前 d i s t [ t ] > 0 dist[t]>0 dist[t]>0 ,在更新距离中,当 j = t j=t j=t 时,他会更新自己的 d i s t [ t ] dist[t] dist[t]为负数 ,那么就不对了,因为我们不考虑负自环

AC核心代码:

#include <bits/stdc++.h>

using namespace std;
#define INF 0x3f3f3f3f
int arr[510][510], dis[510];
bool flag[510];
int n, m, u, v, w;

int prim()
{
    int res = 0;
    memset(dis, 0x3f, sizeof(dis));
    for(int i = 0; i < n; ++i)
    {
        int t = -1;
        for(int j = 1; j <= n; ++j)
            if(!flag[j] && (t == -1 || dis[t] > dis[j]))//这里的dis代表的是这个点到集合的距离
                t = j;

        if(i && dis[t] == INF)//首先排除第一个点,并且得出的t这个点距离集合的距离为INF说明他不会在集合里面,说明这个图不存在最小生成树
            return INF;

        if(i)//第一个点肯定不能加进去,因为这里我们大循环要循环n次,我们是从0开始的
            res += dis[t];//这里要把距离更新写在前面,避免负自环的影响
        for(int j = 1; j <= n; ++j)//更新与t这个点相连的点的距离,因为此时t已经确定可以加入集合了
            dis[j] = min(dis[j], arr[t][j]);
        flag[t] = true;
    }
    return res;
}

int main()
{
    memset(arr, 0x3f, sizeof(arr));
    scanf("%d%d", &n, &m);
    while(m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        arr[v][u] = arr[u][v] = min(arr[u][v], w);
    }

    int t = prim();
    if(t == INF)
        printf("impossible");
    else
        printf("%d", t);
    return 0;
}

K r u s k a l Kruskal Kruskal求最小生成树 ( O ( m l o g n ) ) (O(mlogn)) (O(mlogn))—一般是稀疏图

步骤如下:
1、将所有边按权重从小到大排序 ( O ( m l o g m ) ) (O(mlogm)) (O(mlogm))
2.1 枚举每条边 a → b a→b ab,权重 c c c
2.2 如果 a , b a,b a,b 不连通,那么就将这条边假如到集合中 (并查集来做)
3.最后判断加的边数是否等于 n − 1 n-1 n1

模板题:AcWing 859. Kruskal算法求最小生成树
AC代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n,m,u,v,w,p[MAXN];
struct Edge
{
    int u,v,w;
}edge[MAXN];

bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}

int find(int x)
{
    if(p[x]!=x)
        p[x]=find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)//初始化
        p[i]=i;
    
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&u,&v,&w);
        edge[i]={u,v,w};
    }
    
    sort(edge+1,edge+m+1,cmp);//根据边的权重来排序
    
    int res=0,cnt=0;
    for(int i=1;i<=m;++i)
    {
        int a=edge[i].u,b=edge[i].v,w=edge[i].w;
        a=find(a),b=find(b);
        if(a!=b)
        {
            ++cnt;//边数+1
            p[a]=b;//合并
            res+=w;//加上权重
        }
    }
    
    if(cnt<n-1)//如果边数小于n-1,说明n个点没有全部连通
        printf("impossible");
    else
        printf("%d",res);
        
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值