最小生成树总结

算法总结之最小生成树

首先先来弄清楚几个定义
(1)连通图:在无向图中,任意两点都有路径相通,也就是任意两点都可以走到,则称该图为连通图。

(2)强连通图:在有向图中,任意两点都有路径相通,则称为强连通图。

(3)生成树:n个点,有且只有n-1条边的连通图,称为生成树。生成树不存在环。

(4)最小生成树:边权之和最小的生成树,称为最小生成树。

(5)稠密图:边的数量大约是点的数量的平方级别,一般用邻接矩阵来存。

(6)稀疏图:边的数量小于点的数量的平方级别,一般用邻接表来存。

求最小生成树一般有两种方法,普利姆算法和克鲁斯卡尔算法。两种算法各有优点,一般分别对应于稠密图和稀疏图。

对于给定的n个点,m条边,我们需要选择n-1条构成最小生成树。

(1)普利姆算法:

思想核心:通过迭代n次,每次选择一个集合外距离集合最近的点(这里的集合是指已经完成连通的点的点集),加入集合,用这个点去更新其他点到集合的的距离。因为第一次迭代集合中还没有点,所以可以选择任意一个点作为“初始点”。具体代码如下:(直接在网页上写的代码,可能会有些地方手残代码敲错)

#include <bits/stdc++.h>
using namespcace std;
int g[N][N];//邻接矩阵,用来存图
int n,m;//n个点,m条边
bool st[N];//纪录某个点又没有加入集合
int dis[N];//纪录距离

int prim()//算法实现
{
    memset(dis,0x3f,sizeof dis);//初始化距离
    int res=0;
    for(int i=0;i<n;i++)//迭代n次
    {
        int t=-1;
        for(int j=1;j<=n;j++)//找到集合外距离集合最近的点,st[j]==1表示已经加入集合
        {
            if(!st[j]&&(t==-1||dis[j]<dis[t]))t=j;
        }
        // 当i==0是,是第一次迭代,随便找一个点
        if(i&&dis[t]==inf)return inf;//不连通的情况
        if(i)res+=dis[t];//先加再更新其他点,以防存在有坑的数据存在自环并且权重为负值,这样会把本身的距离变的更小,但根据生成树的定义是没有自环的。
       
        for(int j=1;j<=n;j++)dis[j]=min(dis[j],g[t][j]);//用找到的点去更新集合外的点到集合的最小距离
        st[t]=1;//加入集合
    }
}
int main()
{
    memset(g,0x3f,sizeof g);//某两点之间不一定只读入一条边,保留距离最小的
    
    //读入数据,不具体写了。注意是无向图
    
    int t=prim();
    if(t==0x3f3f3f3f)...//不连通,无法形成最小生成树
    else cout<<t<<"\n";//输出最小生成树的边权之和
  
    return 0;
}
    

(2)克鲁斯卡尔算法:

思想核心:将所以边的权重按从小到大排序,然后遍历所有边,如果两个点不连通,就把他们连通,对于已经连通的,因为是按权重从小到大枚举的,所以在之前进行连通时更新的是最优的,判断最后连通的边是否等于n-1就行了。维护连通块连通性显然用并查集,具体看代码:

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct node{
    int u,v,w;
    bool operator <(const node &W)const  //结构体自定义排序
    {
		return w<W.w;
    }
}a[N];  //存边
int fa[N]; //并查集
int find(int x)
{
    if(fa[x]!=x)return fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    //先读入数据,不具体写了
        
    for(int i=1;i<=n;i++)fa[i]=i;//并查集初始化
    
    int cnt=0;//纪录连通的边数
    int res=0;//纪录权重之和
    for(int i=0;i<m;i++)
    {
		int u=a[i].u,v=a[i].v,w=a[i].w;
        int pu=find(u),pv=find(v);
        if(pu!=pv)
        {
           cnt++;
           res+=w;
           fa[pu]=pv;//使其连通
        }
    }
    if(cnt<n-1)cout<<-1<<"\n";  //根据生成树的定义,n个点,n-1条边。
    else cout<<res<<"\n";
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值