c++最小生成树

本文介绍了Prim算法和Kruskal算法在求解最小生成树问题中的应用,以及使用优先队列和并查集数据结构的实现。Prim算法通过逐步添加节点构建最小生成树,而Boruvka算法则从多个连通分量中选择最短边进行合并。
摘要由CSDN通过智能技术生成

最小生成树

最小生成树问题是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。两种经典的算法是Prim算法Kruskal算法,这里给出两种算法我的实现:

Prim算法

核心在于加点,每次将还不在图中的,和图距离最小的点加到图里,直到所有点都被加到图中或所有边都已经用完退出。这里可以看到和dijkstra算法很相似,区别在于最短路算法是有确定的起点的,每次加的是到这个起点距离最小的点,Prim加的是到当前已经构造好的最小生成树距离最小的点。

(在这两个算法中我们很容易想到的数据结构是优先队列,因为这是一个动态变化的图)

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<int,int> pii;
const int maxn=5005;

int n,m,u,v,w,cnt,len;
pii tmp;
bool flag;
ll sum;
bool vis[maxn];  // 记录点是否已经在图中
int dis[maxn][maxn];  // 距离矩阵
vector<int> tos[maxn];
struct cmp{
    bool operator()(pii a,pii b){
        return a.first>b.first;
    }
};
priority_queue<pii,vector<pii>,cmp> pq;
int main(){
    cin>>n>>m;
    memset(dis,0x3f,sizeof dis);
    while(m--){
        cin>>u>>v>>w;
        dis[u][v]=min(w,dis[u][v]);
        dis[v][u]=dis[u][v];
        tos[u].push_back(v);
        tos[v].push_back(u);
    }
    // 任选一点作为起点,以1为例
    cnt=1;
    vis[1]=1;
    len=tos[1].size();
    for(int i=0;i<len;i++)
        pq.push(pii(dis[1][tos[1][i]],tos[1][i]));
    // 加完了所有点或已经用完了所有边退出
    while(cnt!=n&&!pq.empty()){
        tmp=pq.top();
        v=tmp.second;
        pq.pop();
        if(!vis[v]){
            cnt++;
            vis[v]=1;
            sum+=tmp.first;
            len=tos[v].size();
            for(int i=0;i<len;i++)
                if(!vis[tos[v][i]])
                    pq.push(pii(dis[v][tos[v][i]],tos[v][i]));
        }
    }
    if(cnt==n)cout<<sum<<endl;
    else cout<<"orz\n";
    return 0;
}

Boruvka 算法

初始将所有点都看成一棵树,每次加端点在不同连通分量中的最短的边,直到加了点数-1的边或所有边已经被用完,退出。

连通分量很容易想到并查集,这里不展开说了,代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<int,int> pii;
const int maxn=5005;
typedef long long int ll;
int n,m,u,v,w,cnt;
int pre[maxn];
ll sum;
int findroot(int x){
    int r=x,tmp;
    while(r!=pre[r])
        r=pre[r];
    while(r!=pre[x]){
        tmp=pre[x];
        pre[x]=r;
        x=tmp;
    }
    return r;
}
void join(int x,int y){
    int fx=findroot(x),fy=findroot(y);
    if(fx!=fy)
        pre[fx]=fy;
}
struct edg{
    int u,v,w;
};
edg tmp;
struct cmp{
    bool operator()(edg e1,edg e2){
        return e1.w>e2.w;
    }
};
priority_queue<edg,vector<edg>,cmp> pq;
int main(){
    cin>>n>>m;
    while(m--){
        cin>>tmp.u>>tmp.v>>tmp.w;
        pq.push(tmp);
    }
    for(int i=1;i<=n;i++)
        pre[i]=i;
    while(cnt<n-1&&!pq.empty()){
        tmp=pq.top();
        pq.pop();
        u=tmp.u;
        v=tmp.v;
        w=tmp.w;
        if(findroot(u)!=findroot(v)){
            cnt++;
            sum+=w;
            join(u,v);
        }
    }
    if(cnt==n-1)cout<<sum<<endl;
    else cout<<"orz\n";
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值