【图论】最小生成树——prim算法

 一、什么是最小生成树

最小生成树(Minimum Spanning Tree,MST)

在一个给定的无向图G中求一棵树T

  • 树T拥有图G的所有顶点
  • 所有边都来自图G
  • 使得整棵树的边权最小

 

贪心策略:

prim算法:    让小树长大

kruskal算法:将森林合并成树

二、prim算法 

 与Dijkstra算法区别:

思想几乎完全相同,

Dijkstra算法的最短距离指到源点s的最短距离;

prim算法的最短距离指到集合s的最短距离

 

时间复杂度是O(V^2),

邻接表实现通过堆优化可使时间复杂度降为O(VlogV+E) 

尽量在稠密图上使用

三、程序示例

//邻接矩阵版
//求边权之和ans
#include <iostream>
#define MAXN 510
#define MAXDATA 1000000000
using namespace std;

int G[MAXN][MAXN];
int nv,ne,c1;//顶点数,边数,起始点

int ans;//最小生成树的边权之和
int dis[MAXN];
int collected[MAXN];
void prim(){
    fill(dis,dis+nv,MAXDATA);
    dis[c1]=0;
    for(int i=0;i<nv;i++){
        //1:FindMin
        int minId=-1,minValue=MAXDATA;
        for(int j=0;j<nv;j++){
            if(!collected[j] && dis[j]<minValue){
                minValue=dis[j];
                minId=j;
            }
        }
        if(minId==-1) break;//如果生成树存在,不会中途退出
        collected[minId]=1;
        ans+=dis[minId];//不知道是哪条边,总之是该点到集合s最近的边
        //2:main part
        for(int j=0;j<nv;j++){
            if(!collected[j] && G[minId][j]!=MAXDATA){
                if(dis[j]>G[minId][j]){
                    //不需要经过minId了,只需要到minId最短
                    dis[j]=G[minId][j];
                }
            }
        }
    }

}

int main(){
    scanf("%d %d %d",&nv,&ne,&c1);//顶点数,边数,起始点

    for(int i=0;i<nv;i++){
        for(int j=0;j<nv;j++){
            G[i][j]=MAXDATA;
        }
    }

    int tmp1,tmp2,tmpl;
    for(int i=0;i<ne;i++){
        scanf("%d %d %d",&tmp1,&tmp2,&tmpl);
        G[tmp1][tmp2]=tmpl;
        G[tmp2][tmp1]=tmpl;//无向图
    }

    prim();

    printf("%d",ans);

    return 0;
}
//邻接表版
#include <iostream>
#include <vector>
#define MAXN 510
#define MAXDATA 1000000000
using namespace std;

struct Node
{
    int v;
    int dis;
    Node(int _v,int _dis):v(_v),dis(_dis){}
};


vector<Node> Adj[MAXN];//邻接表
int nv,ne,c1;//顶点数,边数,起始点

int ans;//最小生成树的边权之和
int dis[MAXN];
int collected[MAXN];
void prim(){
    fill(dis,dis+nv,MAXDATA);
    dis[c1]=0;
    for(int i=0;i<nv;i++){
        //1:FindMin
        int minId=-1,minValue=MAXDATA;
        for(int j=0;j<nv;j++){
            if(!collected[j] && dis[j]<minValue){
                minValue=dis[j];
                minId=j;
            }
        }
        if(minId==-1) break;
        collected[minId]=1;
        ans+=dis[minId];//不知道是哪条边,总之是该点到集合s最近的边
        //2:main part
        for(int j=0;j<Adj[minId].size();j++){//邻接点
            int v=Adj[minId][j].v;
            int dist=Adj[minId][j].dis;
            if(!collected[v] && dis[v]>dist){//未收录且需要更新
                dis[v]=dist;
            }
        }
    }

}

int main(){
    scanf("%d %d %d",&nv,&ne,&c1);//顶点数,边数,起始点

    int tmp1,tmp2,tmpl;
    for(int i=0;i<ne;i++){
        scanf("%d %d %d",&tmp1,&tmp2,&tmpl);
        Adj[tmp1].push_back(Node(tmp2,tmpl));
        Adj[tmp2].push_back(Node(tmp1,tmpl));
    }

    prim();

    printf("%d",ans);

    return 0;
}

测试数据(起始点为0):

6 10 0
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3

输出结果:15 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值