问题描述
有一张 \(n\) 个顶点、\(m\) 条边的无向图,且是连通图,求最小生成树。
Prim算法简析
\(Prim\) 算法是一种求最小生成树的算法。
设该图为 \(G = (V, E)\)。最小生成树即所求为 \(G_T = (V_T, E_T)\),因为图是连通的,所以最小生成树会覆盖所有的顶点,即 \(V == V_T\)。\(G_T\) 的真子图 \(G_A = (V_A, E_A)\),构成一棵树(但 \(V_A < V\))。\(G - G_A\) 为 \(G_B = (V_B, E_B)\), 其中 \(V_B = V - V_A\)。
为了这棵树 \(G_A\) 继续生长为最小生成树 \(G_T\),依据贪心策略,我们要挑选权值最小的边加入 \(G_A\)。也就是说,从连接 \(G_A\) 和 \(G_B\) 的边中挑选权值最小的边。
代码实现
#include <bits/stdc++.h>
using namespace std;
#define MAX 10 // 最大定点数
#define INF 1e8
// 定义边
typedef struct
{
int to, worth;
} edge;
typedef pair<int, int> P; // first -- 顶点编号; second -- 该顶点到树的最短距离
vector<edge> G[MAX]; // 邻接链表
int d[MAX]; // i 到树的最小距离
bool vis[MAX]; // i 是否在树中
// 最小堆
struct cmp
{
bool operator()(const P &a, const P &b)
{
return a.second > b.second;
}
};
int prim(void)
{
// 初始化
fill(begin(d), end(d), INF);
d[1] = 0; // 以 1 为根节点
priority_queue<P, vector<P>, cmp> Q;
Q.push(P(1, 0));
int ans = 0; // 最小生成树
while (!Q.empty())
{
P p = Q.top(); // p -- 连接 G_A 和 G_B 的最短边
Q.pop();
int u = p.first;
if (vis[u]) // u 是否在树中
continue; // 若在,则跳过该点
ans += d[u]; // 若不在,加入树中
vis[u] = true; // u 已加入树
// 遍历 u 为起点的每一条边
for (int i = 0; i < G[u].size(); i++)
{
edge e = G[u][i];
if (!vis[e.to] && d[e.to] > e.worth) // 寻找未加入树且最短的边
{
d[e.to] = e.worth; // 更新 e.to 到 G_A 的最短路径
Q.push(P(e.to, e.worth));
}
}
}
return ans;
}
注:
- 1、这里我们选择 \(1\) 为根节点。其实,可以选择任意顶点为根节点。因为 \(G\) 是连通图,所以最终的最小生成树一定覆盖所有的顶点 \(G.V\)。
- 2、该算法的关键是如何找到连接 \(G_A\) 和 \(G_B\) 的最短边,这里,我们采用了最小优先队列(以边的权值为排序依据)。对于 \(G_A\) 的一个顶点 \(u\),遍历从它的所有边,选择符合要求的边加入 \(Q\)。这样,取出的边就是最短边了。
我们来分析一下这里的要求if (!vis[e.to] && d[e.to] > e.worth)
。首先,要明确一点,虽然我们在挑选 \(u\) 的边,但其实,也在挑选边的终点 \(v\)。因此,顶点 \(v\) 必须不在 \(G_A\) 中,这就是条件一。同时,\(e(u, v)\) 可能有重边,不止一条。所以,我们要挑选重边中 \(e(u, v).w\) 最小的,这就是条件二。
仔细看这个 \(if\) 语句,我们会发现,队列里可能依然有 \(e(u, v)\) 的重边,但肯定有 \(e(u, v)\) 的最短边。这里,我们这样处理重边:首先,最小优先队列保证了取出来的一定是最短边;其次,if (vis[u])
这个判断语句跳过了剩下的重边(因为重边都有一个公共顶点 \(v\),然而,由于最小优先队列,它已经加入 \(G_A\))。 - 3、我们可以发现,最小生成树算法 \(Prim\) 与最短路径算法 \(Dijkstra\) 很相似。其中,
d[]
的意义并不一样。Prim
:\(d[u]=\) 顶点 \(u\) 到 \(G_A\) 的最短路径。Dijkstra
:\(d[u]=\) 顶点 \(u\) 到起点 \(s\) 的最短路径。
完
标签:Prim,int,最小,vis,算法,顶点,重边 From: https://www.cnblogs.com/hoyd/p/18008993