最小生成树

今天主要介绍一些最小生成树问题(正边负边权都可以),最小生成树也是一种很重要的数据结构,主要处理最少花费或者最短长度问题; 最小生成树针对无向图

最小生成树算法的主要脉络:

 下面首先我来介绍kruskal算法:(相比prim更加常用 本人蒟蒻)

Kruskal算法基本思想

1) 克鲁斯卡尔 (Kruskal) 算法,是用来求加权连通图的最小生成树的算法 。
2) 基本思想 :按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
3) 具体做法 :首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

原文链接:https://blog.csdn.net/weixin_45829957/article/details/108001882

 Kruskal算法具体做法

  1.  如何处理从小到大并存储边?A:直接调用STL的sort函数,用结构体去存储边和权值
  2. 如何保证已经存到了n-1条边并且不构成回路?A:在模拟结束后进行特判;使用并查集,将祖宗节点不相同的点进行入队操作

例题: 洛谷P3366

#include <iostream>//不用万能头是因为本蒟蒻记不住
#include<algorithm>
using namespace std;
int n,m;
const int   INF=0x3f3f3f3f;

const int N=1e5+10;
const int M=4e5+10;

int fa[N],index;//存储祖宗节点
struct Edge
{
    int e,ne,w;
    bool operator< (const Edge &tmp) const
    {
        return w<tmp.w;
    }
}edge[M];
int cnt,ans;
int find(int x)
{
    if(fa[x]!=x)fa[x]=find(fa[x]);

    return fa[x];
}
//在结构体里可以直接进行重载小于号,也可以自己手写比较函数
//bool cmp(Edge a,Edge b)
//{
//  return  a.w<b.w;
//}
int Kruskal()
{
    sort(edge+1,edge+1+m);//进行排序

    for(int i=1;i<=n;i++)fa[i]=i;//初始化并查集

    for(int i=1;i<=m;i++)
    {
        int a,b,c;

        a=edge[i].e,b=edge[i].ne,c=edge[i].w;

        int nx=find(a);
        int ny=find(b);

        if(nx!=ny)
        {
            ans+=c;
            cnt++;
            //进行合并操作
            fa[nx]=ny;
        }
    }

    if(cnt<n-1)return INF;
    else
        return ans;
}
int main()
{
    cin>>n>>m;

    for(int i=1;i<=m;i++)
    {
        int a,b,c;

        cin>>a>>b>>c;

        edge[i]={a,b,c};
    }

    int t=Kruskal();

    if(t==INF)cout<<"orz"<<endl;
    else
        cout<<t<<endl;

    return 0;
}

接下来介绍一下朴素prim如何写最小生成树的问题

Prim算法基本思想(普利姆算法

 与Dijkstra算法(求最短路)类似,Prim算法主要通过贪心策略从某个点每一次找到边权最小的边,并将这条边的另一个顶点加入最小生成树点集之中,重复n次,直到点集中加入所有顶点

#include <iostream>//不用万能头是因为本蒟蒻记不住
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
const int N = 510, INF = 0x3f3f3f3f;
int dis[N],vis[N],g[1010][1010];
int prim()
{
    memset(dis,0x3f,sizeof dis);//进行一个初始化

    int ans=0;
    dis[1]=0;
    for(int i=0;i<n;i++)
    {
        int t=-1;

        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&(t==-1||dis[t]>dis[j]))
            t=j;
        }
        if(i&&dis[t]==INF)return INF;//当前找到的最短距离为INF说明图不连通无最小生成树 

        ans+=dis[t];
        
        vis[t]=true;

        for(int j=1;j<=n;j++)//进行距离的更新
        {
            dis[j]=min(dis[j],g[t][j]); 
        }
    }
    return ans;
}  

int main()
{
    cin>>n>>m;

    memset(g,0x3f,sizeof g);

    for(int i=1;i<=m;i++)   
    {
        int a,b,c;

        cin>>a>>b>>c;

        g[a][b]=g[b][a]=min(c,g[a][b]);
    }

    int t=prim();

    if(t==INF)cout<<"impossible"<<endl;
    else
        cout<<t<<endl;
    return 0;
}

谢谢浏览❤

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值