最小生成树之Prim算法(详细图解)

作为一个伪ACMer,先来首广为人知的打油诗:
模拟只会猜题意,贪心只能过样例,数学上来先打表,规律一般是DP,组合数学碰运气,计算几何瞎暴力,图论一顿套模板,数论只会GCD,递归递推伤不起,搜索茫然TLE,分治做得像枚举,暴力枚举数第一,数据结构干瞪眼,怒刷水题找信心。

写在前面:

目录:

 写在前面:

 最小生成树的概念 

 经典题目

 prim算法简介 

 prim算法解析 (详细图解)

 代码实现

 代码实战

Kruskal算法一样Prim算法也是解决最小生成树问题的一种经典算法。最小生成树问题是指在一个无向带权图中,找到一棵树,使得树的所有边的权值之和最小,并且包含图中的所有顶点。

Prim算法是一种基于顶点的贪心算法,适用于稠密图。它从任意一个顶点开始,每次选择一个与当前生成树最近的顶点,并将该顶点和相应的边加入生成树中。重复此过程,直到所有顶点都被包含在生成树中。

 最小生成树 的概念 

最小生成树(Minimum Spanning Tree,MST)是图论中的一个经典问题,它指的是在一个加权无向图中找到一个边的子集,使得这个子集构成的树包含图中的所有顶点,并且边的权重之和最小。

说人话就是在一个有n个顶点的稠密图中用n-1条边连接n个顶点,然后每条边都带有权值,求满足条件的这n-1条边的总的权值和的最小值。

最小生成树在网络设计、电路设计等领域有着广泛的应用。

下面我们以这个图为例,用Prim算法求它的最小生成树:

            

最后输出: 最小的权值和 res=38

#include<bits/stdc++.h>

using namespace std;
// typedef long long int;
#define int long long 
const int N = 1005;
const int inf = 1e9+10;
int dist[N],res=0; //dist[]储存到集合S的距离,res保存结果。

int g[N][N]; // g[i][j]表示顶点i和顶点j之间的权值为g[i][j]

bool vis[N];//用来标记这个顶点是否已选过
int n,m; // n个顶点 m条边
void prim(){
    vis[1]=1; //把点1加入集合S,点1在集合S中,将它到集合的距离初始化为
    dist[1]=0; // 从顶点1出发
    for(int i=2;i<=n;i++)dist[i]=min(dist[i],g[1][i]); // 用顶点1去更新它周围顶点的距离
    for(int i=2;i<=n;i++){
        int t=-1;// 初始化下标
        int cnt=inf;//初始化距离
        for(int j=2;j<=n;j++){
            if(!vis[j]&&dist[j]<cnt){
                t=j;
                cnt=dist[j]; // 存储最小的权值
            }
        }
        if(t==-1){ 
            res=inf; // 说明没找到 构造不了最小生成树 终止
            return ;
        }
        res+=cnt; // 总权值
        vis[t]=1; // 标记
        for(int j=2;j<=n;j++)dist[j]=min(dist[j],g[j][t]); ////用新加入的点来更新dist[]
    }
}

void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=inf; //先初始化每两个顶点间的距离为无穷大 
        }
        dist[i]=inf;
    }
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        g[u][v]=g[v][u]=w; //由于是无向边,我们对g[a][b]和g[b][a]都要赋值
    }
    prim();
    if(res==inf)//如果res的值是正无穷,表示不能该图不能转化成一棵树,输出orz
        cout<<"orz";
    else
        cout<<res;//否则就输出结果res
}

signed main(){
    freopen("input.in", "r", stdin);
    freopen("out.txt", "w", stdout);
    int _=1;
    while(_--){
        solve();
    }
    return 0;
}

代码实战

"纸上得来终觉浅,绝知此事要躬行".也许看完了上面的解析,你已经最prim算法有了一个大致的了解,学习算法,大致的了解是远远不够的,代码的实践永远是最重要的,学习完一个算法后一定要去自己亲手试试,每个人都有自己的代码风格,大家大可以在自己的风格之上写出自己prim。
Prim简介难度
P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)模版题★☆☆☆☆
P1967 [NOIP2013 提高组] 货车运输 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)基本应用★☆☆☆☆
P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)基本应用★☆☆☆☆
 第一题是模板题,后面两题主要是更好得帮助我们理解最小生成树——prim在实际和题目中得应用。

最后感谢未来的大犇你能够看到最后,也希望各位能支持一下本彩笔(1赞十道1500算法题)Orz

写在最后:

### Kruskal 和 Prim 算法图解过程 #### Kruskal 算法图解 Kruskal 算法是一种用于寻找加权无向图中的最小生成树的方法,该方法基于贪心策略。算法的主要思想是在每一步选择当前可用边集中权重最小的一条边来构建最终的最小生成树。 1. 将所有的边按照其权重升序排列。 2. 初始化一个空的结果集用来存储构成最小生成树的边。 3. 遍历排序后的边列表,对于每一遍遇到的新边 `(u, v)` ,如果顶点 `u` 和 `v` 不在同一棵树内,则将这条边添加到结果集中,并连接这两个顶点所属的不同子树形成更大的一棵树;否则跳过此边以防止形成环路[^2]。 为了更直观地展示这一过程,可以考虑如下简单例子: 初始状态下的图形表示为多个独立节点,随着处理过程中不断选取合适的边并将其加入到正在形成的MST中直到所有节点都被连通为止。 ```plaintext Step 0 (Initial State): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ... (逐步增加边的过程省略) Final MST: All nodes connected without cycles. ``` #### Prim 算法图解 Prim 算法则从任意选定的一个起始结点出发,通过迭代方式逐渐扩展已有的部分生成树直至覆盖整个网络。具体来说, 1. 任选一顶点作为起点,初始化只含有这单一顶点的部分生成树; 2. 找出与现有部分生成树相连但尚未被收录进来的那些边上具有最低成本者,并把它所关联的那个新顶点也纳入进来; 3. 重复上述步骤直到所有的顶点均已被访问完毕[^1]。 下面给出一个简化版的Prim算法执行流程示意: 同样是从完全分离的状态开始,每一次循环都会挑选一个新的最佳候选边以及相应的邻近顶点加入现有的生长着的MST之中,最后达到全网联通而没有任何回路的效果。 ```plaintext Step 0 (Start with any vertex say 'A'): A | B C D ... Step n (After adding several edges and vertices gradually forming the tree structure): ... -> A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z <- ... ``` 两种算法虽然采取了不同的路径去解决问题,但是它们的目标都是找到能够连接给定图上全部顶点的同时使得总长度尽可能小的那一组特定边组合——这就是所谓的“最小生成树”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值