最小生成树——Prime Kruskal

本文深入探讨了两种经典的最小生成树算法:Prim算法和Kruskal算法。通过代码实例详细讲解了它们的核心思想,Prim算法以点为参照,逐步构建树;而Kruskal算法以边为参照,通过排序和并查集避免环路,最终形成一棵包含所有节点的树。适用于无负权边的图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Prime

算法核心:以点为参照,依次找到距离树最近的点,并入树中。无负边。

#include <iostream>
#include <cstring>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 510;
int d[N];       //其他点到树上的距离
int g[N][N];    //已知的边长
bool flag[N];   //记录是否到树上
int n , m;

int Prime(){
    //不连通返回INF 否则返回ans
    int ans = 0;
    memset(d,INF,sizeof d);
    for(int i = 0 ; i<n ; i++){
        int t = -1;                              
        for(int j = 1 ; j<=n ; j++){     //找到树距离最小的点
            if(!flag[j]&&(t==-1||d[j]<d[t]))  //第一趟t=-1先忽略 因为d[t]没有初值
                t = j;
          }  
            if(i&&d[t]==INF)  return INF;     //树的第一个点还没有更新边 所以d[0]肯定是INF,所以先跳过
            if(i) ans += d[t];                
            flag[t] = true;
           
        
        for(int j = 1 ; j<=n ; j++) d[j] = min(d[j] , g[t][j]);  //因为加入了新点 所以更新d数组
    }
    return ans;
    
}

int main(){
     
     cin>>n>>m;
    
     memset(g,INF,sizeof g);
    //如果被录入则为true所以存在自环没什么影响
     while(m--){
         int x,y,z;
         cin>>x>>y>>z;
         
         g[x][y] = g[y][x] =min(z , g[x][y]);  //无向 因为存在重边 所以要取最小值
         
         }
         int k = Prime();
         
         if(k==INF) cout<<"impossible"<<endl;
         else cout<<k<<endl;
     
    return 0;
}

Kriskal

算法核心:以边为参照,将边按升序排序,然后找出最短的边组成完整的树。需要用到并查集。无负边

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5+10 , M = 2e5;
const int INF = 0x3f3f3f3f;
int pre[N];
int m,n;

struct Edge{          //用于读取数据
    int a,b,w;

    bool operator<(const Edge &W) const {  	//重载运算符 ,保证长度顺序
        return w<W.w;
    }
}Edge[M];

int find(int x){         //查并集核心 
    if(pre[x]!=x) pre[x] = find(pre[x]);
    return pre[x];
}

int Kruskal(){
    sort(Edge+1,Edge+m+1);	//对长度排序
    for(int i = 1 ; i<=n ; i++) pre[i] = i;   //初始化查并集

    int ans = 0 , s = 0;    //ans是总长度 ,s是并入树的边
    
     for(int i = 1 ; i<=m ; i++){    
        auto t = Edge[i];
        t.a = find(t.a);            //对一条边的两个点分别找祖宗
        t.b = find(t.b);
        if(t.a!=t.b){		    //如果不是同一个祖宗则不会形成环
            pre[t.a] = t.b;	    //可以相连
            ans+=t.w;
            s++;
        }
    }
    if(s!=n-1) return INF; 	   //如果并入树的边数不等于点的个数减一说明没有连通
     return ans;
}


int main()
{

    cin>>n>>m;
    for(int i = 1 ; i<=m ; i++){
        int x,y,z;
        cin>>x>>y>>z;
       Edge[i] = {x,y,z};
    }
    int k = Kruskal();
    if(k==INF) cout<<"impossible"<<endl;
    else cout<<k<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值