Prim算法(最小生成树)

首先给出子图的定义:

子图:

设G = <V, E>,G' = <V', E'>为两个图(同为无向图or有向图)

如果V' ⊆ V && E' ⊆ E,则称G'为G的子图,G称为G'的母图,记作G' ⊆ G。

如果V' = V,则称G' 为 G 的生成子图(用到了所有的点 但不一定用到所有的边的子图)

如果G'是无向图 并且是一棵树的结构,则称为生成树

(树的结构:n个点&&n-1条边)

换句话说,如果这个生成子图是n-1条边,那么就称之为 生成树(是无向图)。


 

最小生成树:

一张有权无向连通图中,边权和最小的生成树叫做最小生成树

如果原图不连通,则没有最小生成树。

 

Prim算法是一种常用的最小生成树算法,做法与Dijkstra相似,

也需要维护一个顶点集合C,每次通过一条边连接一个不在C中的点

直到最后形成一个树形结构。

 

定义dist(u)表示 连接C中任意点和u的边的边权的最小值

具体流程如下:

将任意点x加入C,此时C中只有一个点x;

对于其他点y,如果x, y之间有边,则dist(y) = min(这些边的边权),否则dist(y) = INF;

在每一轮中,将dist最小(不能是INF)还不在C中的顶点z加入C,

同时,将连接C和z的边权最小的边加入最小生成树的边集合之中,

并且用z连出去的边尝试更新其他点的dist;

当没有新的点能加入C时,该算法结束。

此时,若所有的点都加入了C,则找到了最小生成树,否则说明该图不连通。


 

朴素Prim代码实现:O(n2+m)

复制代码
 1 struct Node{
 2     int y, v;
 3     Node(int _y, int _v){   //构造函数
 4         y = _y;
 5         v = _v;
 6     };
 7 };
 8 
 9 vector <Node> edge[N + 1];  //边集
10 int n, m, dist[N + 1];      //dist集
11 bool b[N + 1];              //存在性集
12 
13 int Prim(){
14     memset(b, false, sizeof(b));
15     memset(dist, 127, sizeof(dist));
16     dist[1] = 0;
17     int ans = 0, tot = 0;
18     while(1){
19         int x = -1;
20         for (int i = 1;i <= n;i++){
21             if (!b[i] && dist[i] < 1 << 30){
22                 if (x == -1 || dist[i] < dist[x]){
23                     x = i;
24                 }
25             }
26         }
27         if (x == -1){
28             break;
29         }
30         ++tot;
31         ans += dist[x];
32         b[x] = true;
33         for (auto i : edge[x]){
34             dist[i.y] = min(dist[i.y], i.v);
35         }
36     }
37     if (tot != n){
38         return -1;
39     }
40     else{
41         return ans;
42     }
43 }
复制代码

堆优化Prim算法:O((n+m)log n)

复制代码
 1 struct Node{
 2     int y, v;
 3     Node(int _y, int _v){   //构造函数
 4         y = _y;
 5         v = _v;
 6     };
 7 };
 8 
 9 set < pair<int, int> > q;
10 
11 vector <Node> edge[N + 1];  //边集
12 int n, m, dist[N + 1];      //dist集
13 bool b[N + 1];              //存在性集
14 
15 int Prim(){
16     memset(b, false, sizeof(b));
17     memset(dist, 127, sizeof(dist));
18     dist[1] = 0;
19     q.clear();
20     for (int i = 1;i <= n;i++){
21         q.insert(mp(dist[i], i));
22     }
23     int ans = 0, tot = 0;
24     while(!q.empty()){
25         int x = q.begin()->second;
26         q.erase(q.begin());
27         if (dist[x] > 1 << 30){
28             break;
29         }
30         ++tot;
31         ans += dist[x];
32         b[x] = true;
33         for (auto i : edge[x]){
34             if (!b[i.y] && i.v < dist[i.y]){
35                 q.erase(mp(dist[i.y], i.y));
36                 dist[i.y] = i.v;
37                 q.insert(mp(dist[i.y], i.y));
38             }
39         }
40     }
41     if (tot != n){
42         return -1;
43     }
44     else{
45         return ans;
46     }
47 }
复制代码

 

posted @   Conqueror712  阅读(300)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示