最小生成树------prim与Kruskal

最小生成树概念:

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 [1]  最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

白话文:如何用总长度尽量小的边连通顶点为n的图使之为一个连通图,并求出所有边长总和。

1.prim

#include<bits/stdc++.h> 
using namespace std;

const int MAXN = 5*1e3+5,INF = 0x3f3f3f3f;//定义一个INF表示无穷大。
int g[MAXN][MAXN],dist[MAXN],n,m,res;
//我们用g[][]数组存储这个图,dist[]储存到集合S的距离,res保存结果。
bool book[MAXN];//用book数组记录某个点是否加入到集合S中。


void prim()
{
    dist[1] = 0;//把点1加入集合S,点1在集合S中,将它到集合的距离初始化为0
    book[1] = true;//表示点1已经加入到了S集合中
    for(int i = 2 ; i <= n ;i++)dist[i] = min(dist[i],g[1][i]);//用点1去更新dist[]
    for(int i = 2 ; i <= n ; i++)
    {
        int temp = INF;//初始化距离
        int t = -1;//接下来去寻找离集合S最近的点加入到集合中,用t记录这个点的下标。
        for(int j = 2 ; j <= n; j++)
        {
            if(!book[j] && dist[j]<temp)//如果这个点没有加入集合S,而且这个点到集合的距离小于temp就将下标赋给t
            {
                temp = dist[j];//更新集合V到集合S的最小值
                t = j;//把点赋给t
            }
        }
        if(t==-1){res = INF ; return ;}
        //如果t==-1,意味着在集合V找不到边连向集合S,生成树构建失败,将res赋值正无穷表示构建失败,结束函数
        book[t] = true;//如果找到了这个点,就把它加入集合S
        res+=dist[t];//加上这个点到集合S的距离
        for(int j = 2 ; j <= n ; j++)dist[j] = min(dist[j],g[t][j]);//用新加入的点更新dist[]
    }
}


int main()
{
    cin>>n>>m;//读入这个图的点数n和边数m
    for(int i = 1 ; i<= n ;i++)
    {
        for(int j = 1; j <= n ;j++)
        {
            g[i][j] = INF;//初始化任意两个点之间的距离为正无穷(表示这两个点之间没有边)
        }
        dist[i] = INF;//初始化所有点到集合S的距离都是正无穷
    }
    for(int i = 1; i <= m ; i++)
    {
        int a,b,w;
        cin>>a>>b>>w;//读入a,b两个点之间的边
        g[a][b]=min(g[a][b],w);//由于是无向边,我们对g[a][b]和g[b][a]都要赋值
        g[b][a]=min(g[b][a],w);//重边看情况,取最小值 
    }
    prim();//调用prim函数
    if(res==INF)//如果res的值是正无穷,表示不能该图不能转化成一棵树,输出orz
        cout<<"orz";
    else
        cout<<res;//否则就输出结果res
    return 0;
}

先将点分为S集合和V集合

通过book数组标记此点是否已经加入了S集合,再通过新加入的点来更新dist数组,最终返回res

注意(新加入的点一定是距离S集合最近的点),一个一个加

具体:最小生成树——Prim算法(详细图解)_skynesser的博客-CSDN博客

2.Kruskal算法(最小生成树)

与prim不同,kruskal算的是边,prim枚举的是点.

通过并查集实现。

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 100010, M = 200010, INF = 0x3f3f3f3f;

int n, m;
int p[N];
struct edge{
	int a, b, w;
	edge(){}
	edge(int _a, int _b, int _w){
		a = _a, b = _b, w = _w;
	}
} Edges[M];

long long res = 0, cnt = 0;

int find(int x){                            //并查集 
	if(p[x] != x) p[x] = find(p[x]);   //当自己不是祖宗节点时 
	return p[x];                       //当自己是祖宗节点时,返回自己的编号 
}
bool cmp(edge a, edge b){
	return a.w < b.w;
}
int Kruskal(){                         //Kruskal最小生成树算法
	sort(Edges, Edges + m, cmp);            //Kruskal从小到大找边 
	for(int i = 1; i <= n; i++) p[i] = i; //并查集初始化,每个节点都指向自己
	for(int i = 0; i < m; i++){
		int a = Edges[i].a, b = Edges[i].b, w = Edges[i].w; 
		a = find(a), b = find(b);     //分别找到这条边两个顶点
		if(a != b){
			p[a] = b;
			res += w;
			cnt++;
		}
	}
}

int main(){
	cin >> n >> m;
	for(int i = 0; i < m; i++){
		int a, b, c;
		cin >> a >> b >> c;
		Edges[i] = edge(a, b, c);
	}
	Kruskal();
	if(cnt < n-1) cout << "orz" << endl;
	else cout << res;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值