ACM入门-最小生成树及其应用

前置知识

图的存储、并查集

什么是最小生成树?

今天要讲解的算法是最小生成树,那么什么是最小生成树呢?由于是面向新手的算法教程,尽量用大白话来讲清楚最小生成树这个定义。
假定有这么一个问题,有6个城市,城市之间有一些道路,修道路需要花费不同的金额,现在要你选择其中一些道路,使得所有城市可以互相到达(联通)且花费最小。这个问题的答案就是最小生成树,我们把这个问题抽象为一张图,每个城市是一个点,城市与城市的道路是一条无向边。
在这里插入图片描述

如何求解这个问题?

Prim算法

算法思路:将所有城市分为两个集合,一个集合X是已经在最小生成树中的城市,另外一个集合S是不在最小生成树的城市。一开始所有的城市都在集合S中,集合X为空,第一步任意选择一个点加入X,接着是一个循环,每次选择集合S的城市中离集合X中城市最近的一个城市,加入X中并把这条道路加入最小生成树中直到所有点都加入X中。
在这里插入图片描述
如上图,首先X为空集,挑选A作为起始起点,此时有3个城市B、C、D与A联通{A<->{B,C,D}}其权值分别为{6,5,1},我们选择道路A<->C,将C加入X中,此时可选择的边有{A<->{B,D},C<->{B,D,E,F}}同样的我们选择最短一条边,将F加入X,如此反复。

代码

#include<bits/stdc++.h>
using namespace std;
int mp[105][105],st[105],d[105];//mp存储无向边,st表示该城市是否被加入集合X,d表示 集合S到集合X的花费值
int n;
int prim()
{
    memset(d,0x3f,sizeof(d));//初始化X无点,集合S和集合X不连通
    int res=0;
    d[1]=0;//选择1号点加入集合X,花费为0
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
        {
            if(st[j]==0&&(t==-1||d[t]>d[j]))t=j;//如果点在X中就不要重复选了,选择一个集合X外最小的点
        }
       // cout<<t<<" ";
        st[t]=1;//入集合X
        res+=d[t];//这条边加入最小生成树中
        for(int j=1;j<=n;j++)
        d[j]=min(d[j],mp[t][j]);//加入新点后,更新一下两个集合之间的距离
    }
    return res;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    cin>>mp[i][j];
    cout<<prim();
}

Kruskal算法

算法思路:将所有边排序,每次选择最短的一条边,如果这条边连接的两个点不在同一个集合中,将两点相连。
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
struct edge{
    int a,b,w;//边的起点和终点以及权值
    bool operator <(const edge&t)const{//由小到大排序
        return w<t.w;
    }
}e[1005];
int p[1005];
int find(int x)//并查集来快速判断两点是否在一个集合
{
    if(p[x]!=x)return p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)//读入边
    {
        int a,b,c;
        cin>>a>>b>>c;
        e[i]={a,b,c};
    }
    sort(e+1,e+1+m);//排序
    for(int i=1;i<=n;i++)
    p[i]=i;
    int res=0;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        a=find(e[i].a),b=find(e[i].b),c=e[i].w;
        if(a!=b)p[a]=b,res+=c;//如果两个点不在同一个集合,连上边。
       
    }
    cout<<res;
    
    
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值