问题描述
有一张 \(n\) 个顶点、\(m\) 条边的无向图,且是连通图,求最小生成树。
算法简析
\(Kruskal\) 是一种求最小生成树的算法。
设该图为 \(G = (V, E)\)。最小生成树即所求为 \(G_T = (V_T, E_T)\),因为图是连通的,所以最小生成树会覆盖所有的顶点,即 \(V == V_T\)。\(G_T\) 的真子图 \(G_A = (V_A, E_A)\),\(V_A == V\),构成森林(注意,这里与 \(Prim\) 并不一样。\(G_A\) 初始状态就覆盖所有顶点,每个顶点各自为一棵树)。
因为 \(G_A\) 已经覆盖了所有顶点,所以 \(G - G_A\) 只剩下 \(G\) 中所有的边 \(E\)。为了让 \(G_A\) 从森林变成一棵树,我们需要从 \(E\) 中挑选边加入 \(G_A\)。例如,我们选择 \(e(u, v)\) 加入 \(G_A\),本来独立的 \(u\) 和 \(v\) 被连接起来,相当于 \(u\) 和 \(v\) 各自所在的两棵树要合并成一棵树。
为了得到最小生成树,我们采用贪心策略,每次都从 \(E\) 中挑选最短的边 \(e(u, v)\),如果 \(u\) 和 \(v\) 还不在一棵树上,就将该边加入 \(G_A\),同时合并两棵树。
为了能够高效地合并树,我们采用并查集。
代码
typedef struct
{
int from, to, worth;
} edge;
vector<edge> E;
// Kruskal
bool cmp(const edge &a, const edge &b)
{
return a.worth < b.worth;
}
int kruskal(void)
{
int ret = 0;
init();
sort(E.begin(), E.end(), cmp); // 升序
for (int i = 0; i < E.size(); i++)
{
edge e = E[i];
if (!same(e.from, e.to)) // 判断是否在一棵树中
{
ret += e.worth;
unite(e.from, e.to); // 合并树
}
}
return ret;
}
完
标签:int,Kruskal,一棵树,算法,edge,顶点,worth From: https://www.cnblogs.com/hoyd/p/18009631