浅谈最小生成树算法(c++)

引言

看着一章的标题,很明显和树有关,所以我们就得先知道什么是树。

百度百科里说树是无环的连通图。树也是任意两个结点间有且只有一条路径的图。概而言之,树首先是一个图,其次当中没有环,就这么简单。你想象一下大树的照片(除了一些畸形的枝条融合了)就和“树”很像。

 最小生成树

我们再来说一下最小生成树。给你一个图,让你去除一些边后,成为一个树,并且边权和最小。

例子

原图

生成的树

应用

我们在城市之间修公路,显然,修成一个环就会很费钱,怎么修能即联通所有城市,又使总花费最小?这时候就能用到最小生成树了

prim

概述

prim算法很像Dijkstra算法,还不会Dijkstra的朋友可以看一下我的上一篇文章。

因为生成的树中,每个点都包含其中,所以我们从任意一个点开始搜索(推荐用1号点,当然,你要是一身反骨非得用别的点我也没意见),找到最短边,把最短边的权累加进答案里,标记这个点,修改dist数组

例题

题目描述

样例输入

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5050,M=2e5+100;
struct node{
    int  next,v,w;
}e[M];
 int head[M],cnt=0;
 void add(int u,int v,int w){
    e[++cnt]={head[u],v,w};
    head[u]=cnt;
 }
 struct lis{
    int u,d;
 };
 bool operator<(const lis &x,const lis &y){
    return x.d>y.d;
 }
 priority_queue<lis> q;
 int dis[N],vis[N];
 int res=0,tot=0;
 void Prim(int n){
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    q.push({1,0});
    while(!q.empty() ){
        if(tot>=n)break;
        int u=q.top().u,d=q.top().d;
        q.pop() ;
        if(vis[u])continue;
        vis[u]=1;
        ++tot;
        res+=d;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].v,w=e[i].w;
            if(w<dis[v]){
                dis[v]=w;
                q.push({v,w}); 
            }
        }
    }
 }
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    Prim(n);
    cout<<res<<endl;
    return 0;
}

Kruskal

由prim的算法流程易知,他是不断地选择并加入点,因而也被称为“加点法”

概述

Kruskal按边权从小到大排序,每次取出最短的边,用并查集判断边的两个端点是否连通。不连通就加进并查集,更新答案。

例题

题目描述

样例输入

代码 

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
struct node{
    int u,v,w;
}e[N];
bool cmp(node a,node b){
    return a.w<b.w;
}
int fa[N];
void init(int n){
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
}
int find(int x){
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);;
}
void add(int x,int y){
    x=find(x);
    y=find(y);
    fa[y]=x;
}
int main()
{
    int n,m;
    cin>>n>>m;
    init(n);
    for(int i=1;i<=m;i++){
        cin>>e[i].u>>e[i].v>>e[i].w;
    }
    sort(e+1,e+1+m,cmp);
    int cnt=0;
    int ans=0;
    for(int i=1;i<=m;i++){
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(find(u)!=find(v)){
            ans+=w;
            add(u,v);
            cnt++;
        }
        if(cnt==n-1){
            break;
        }
    }
    cout<<ans<<endl;
    return 0;
}

这是我的第二篇文章,如有纰漏也请各位大佬指正

辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值