最小生成树算法及例题整理

最小生成树是无向图中的算法,最基础的是在一张无向图中求一棵树,该树包含n个点,同时树上所有边的边权和最小。

性质:
包含n个点,n-1条边,
任意两点之间都是连通的,
边权和最小,
可能不止一个,
但是边权和是固定的。

有两种算法可以实现:Prim(普利姆算法)和kruskal(克鲁斯卡尔算法)

Prim

时间复杂度:O(n^2)

Prim算法的本质是从任意一点开始往外扩展,将点放进集合中,同时每次循环找到不在集合中且距离集合最近的点(这里也是它和dijkstra算法的区别,dijkstra算法每次循环找的是距离起点最近的点),把它加进集合,统计将这条边计入最短路,然后再用它去更新其他的点,当所有点都被加进集合的时候,那么就找到了一棵最小生成树。

int g[][],st[][];
int d[];
int res;
void prim()
{
    memset(d,0x3f,sizeof d);
    d[1]=0;//这里其实无所谓,从任意一点开始都可以
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||d[j]<d[t])) t=j;

        st[t]=1;
        res += d[t];
        for(int i=1;i<=n;i++)
            if(d[i]>g[t][i])//t已经放入集合中了,所以g[t][i]才是距离集合的距离
                d[i]=g[t][i];
    }
}

ps:乍一看真的和朴素版dijkstra算法很像,唯一的区别就在距离的更新上

kruskal

时间复杂度:O(mlogm)

这个算法的思路就是将所有的边排序,然后从小到大遍历所有的边,如果这条边连接的两点不在一个集合中,那么就将它们并成一个集合,否则就略过这条边。所以这里的时间复杂度是sort排序的时间,实际上如果m不大的话,可以视为O(m)。另外这种算法比较有趣的一点在于边可以用任意方式存,最简单的就是直接用结构体来存。

struct edge{
    int a,b,c;
}e[];
bool cmp(edge x,edge y)
{
    return x.c<y.c;
}
int p[];
int find(int x)
{
   if(p[x]!=x) p[x]=find(p[x]);
   return p[x];
}
int main()
{
    ...
    sort(e+1,e+m+1);
    int res=0;
    for(int i=1;i<=m;i++)
    {
        int a=e[i].a,b=e[i].b,c=e[i].c;
        a=find(a),b=find(b);//此时a变成a的祖先节点,b变成b的祖先节点
        if(a!=b) p[a]=b,res+=c;//直接将两个并查集的祖先节点合到一块儿
    }
    ...
}

一般来说,可以用prim算法的都可以用kluskal算法,但能用kluskal算法的却未必能用prim算法。

1140. 最短网络(活动 - AcWing

这题其实很裸,连接所有点,边权和最小,那么就是求最小生成树。这题给的是邻接矩阵,所以prim算法要更方便一些。

#include<bits/stdc++.h>
using namespace std;
int g[120][120],n,res,d[120],st[120];
void prim()
{
    memset(d,0x3f,sizeof d);
    d[1]=0;
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||d[t]>d[j]))t=j;
        st[t]=1;
        res += d[t];
        for(int j=1;j<=n;j++)
            if(d[j]>g[t][j]) 
                d[j]=g[t][j];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&g[i][j]);
    prim();
    cout<<res;
}

 1141. 局域网(活动 - AcWing)

思路:边都是无向边,我们要去除一些网线使得网络中没有回路且不影响连通性,不影响连通性那么就是原本连通的去完还是连通的,然后还要没有回路,那么就是树,但是要注意一点,原本各个点未必都是连通的,所以我们最后得到的相当于是一个森林。然后去除的最大,那么留下的就是最小的,我们可以形象的称之为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值