什么是最小生成树

在一张带权无向连通图 $G=(V,E,w)$ 中,最小生成树(Minimum Spanning Tree, MST)是连接所有顶点、且边权和最小的一棵生成树(含 $|V|-1$ 条边、无环).若图不连通,则对应为最小生成森林.

性质

  • 割性质(Cut Property):任取把顶点分成两部分的割,跨越该割的最轻边一定属于某个 MST.

  • 环性质(Cycle Property):任取一个环,其中的最重边不属于任何 MST.

  • 最小瓶颈:任一 MST 同时也是最小瓶颈生成树(最大边权尽可能小).

  • 唯一性:若所有边权互不相同,MST 唯一.

  • 权值平移/缩放:边权统一加同一常数或乘正数,不改变 MST 结构.

  • 允许负边:允许负权,算法与性质不变(只要图无向且可连通).

相关证明

设定 令 $G=(V,E,w)$ 为有限的带权无向连通图,权函数 $w:E\to\mathbb{R}$.生成树 $T\subseteq E$ 指在 $(V,E)$ 上无环且连通、含 $|V|-1$ 条边的子图.记 $\mathrm{MST}(G)$ 为所有使 $\sum_{e\in T}w(e)$ 最小的生成树集合.对任意割 $(S,\bar S)$($S\subset V,\ S\neq\varnothing,\ S\neq V$)称跨越该割且权值最小的边为该割的一条轻边.以下结论均在该设定下成立.

命题 1(割性质)

若 $e$ 是某个割 $(S,\bar S)$ 的轻边,则存在 $T^\star\in\mathrm{MST}(G)$ 使得 $e\in T^\star$.

证明 取任意 $T\in\mathrm{MST}(G)$.若 $e\in T$ 则命题已证.若 $e\notin T$,则 $T\cup\{e\}$ 含唯一简单环 $C$.环 $C$ 必含一条跨越同一割的边 $f\in T$,否则 $C$ 不可能穿越该割回到原侧.由 $e$ 为该割轻边知 $w(e)\le w(f)$.令 $T' = T\cup\{e\}\setminus\{f\}$ 则 $T'$ 为生成树且 $\sum_{T'} w \le \sum_T w$.由 $T$ 的最优性得 $\sum_{T'} w=\sum_T w$,从而 $T'\in\mathrm{MST}(G)$ 且含 $e$.证毕.

命题 2(环性质)

设 $C$ 为图中一环.若 $e\in C$ 满足 $w(e)>\max\{w(g):g\in C\setminus\{e\}\}$(即 $e$ 在 $C$ 上严格最重),则 $e\notin T$ 对一切 $T\in\mathrm{MST}(G)$.

证明 反设存在 $T\in\mathrm{MST}(G)$ 且 $e\in T$.删去 $e$ 后 $T\setminus\{e\}$ 分成两连通块,环 $C\setminus\{e\}$ 中必有某边 $f$ 跨越这两块.由严格最重 $w(f)<w(e)$.令 $T' = T\setminus\{e\}\cup\{f\}$ 得 $\sum_{T'} w < \sum_T w$,与 $T$ 的最优性矛盾.证毕.

命题 3(MST 即最小瓶颈生成树)

设对生成树 $T$ 的“瓶颈”定义为 $\beta(T)=\max_{e\in T}w(e)$.则任意 $T^\star\in\mathrm{MST}(G)$ 皆使 $\beta(T^\star)$ 在所有生成树中取最小值.

证明 取任意 $T^\star\in\mathrm{MST}(G)$,令 $b=\beta(T^\star)$.考虑由所有权值 $<b$ 的边构成的子图,其连通块个数记为 $c$.由于 $T^\star$ 中权值 $<b$ 的边最多把图连成 $c$ 块,剩余为连通必须再选 $c-1$ 条跨块的边,且这些边的权值都$\ge b$.故任一生成树 $T$ 至少含一条权值 $\ge b$ 的边,于是 $\beta(T)\ge b=\beta(T^\star)$.因而 $T^\star$ 瓶颈最小.证毕.

命题 4(唯一性)

若所有边权互不相同,则 $\mathrm{MST}(G)$ 唯一.

证明 反设存在不同的 MST,记为 $T_1\neq T_2$.取对称差 $(T_1\triangle T_2)$ 中权值最小的一条边 $e$.不妨 $e\in T_1\setminus T_2$.将 $e$ 加入 $T_2$ 得一环 $C$,其中必存在 $f\in C\cap T_2$ 且 $f\notin T_1$.由最小性 $w(e)<w(f)$.令 $T_2' = T_2\cup\{e\}\setminus\{f\}$,则 $\sum_{T_2'} w < \sum_{T_2} w$,与 $T_2$ 为最小生成树矛盾.证毕.

命题 5(权值平移/缩放不改变解的结构)

对任意常数 $c\in\mathbb{R}$ 与任意正数 $\alpha>0$,令新权函数

$$w_c(e)=w(e)+c,\qquad w_\alpha(e)=\alpha\,w(e).$$

则 $\mathrm{MST}(V,E,w)=\mathrm{MST}(V,E,w_c)=\mathrm{MST}(V,E,w_\alpha)$.

证明 对任一生成树 $T$,有

$$\sum_{e\in T} w_c(e)=\sum_{e\in T} w(e)+c(|V|-1),\qquad \sum_{e\in T} w_\alpha(e)=\alpha\sum_{e\in T} w(e)$$

前者在不同 $T$ 间仅差一个与 $T$ 无关的常数,后者在不同 $T$ 间做同一正比例缩放,均不改变极小化者的集合.证毕.

命题 6(负权可行)

若允许某些边 $e$ 取负权,所有上述结论与标准算法(如 Kruskal/Prim)的正确性保持不变.

证明 由命题 5,取 $c>-\min_{e\in E} w(e)$ 并考虑 $w_c=w+c$,则 $w_c\ge 0$.对 $(V,E,w_c)$ 的最小生成树与对 $(V,E,w)$ 的最小生成树相同,且割/环性质仅依赖相对比较与交换论证,不依赖权是否为负.于是结论对负权同样成立.证毕.

Kruskal算法

这是Kruskal算法的核心思想

“按权值从小到大选边,能连就连,成环就跳过。”

至于为什么要成环就跳过,因为生成树不能有环,而且一旦出现环,总权值不会变小,只会变大,如果非要证明我要引用一下

给定无向连通图 $G=(V,E,w)$。我们证明“成环的边中,最重的那一条不属于某棵最小生成树(至多在权值并列时可被任意一条同重边替换),因此对按从小到大扫边的 Kruskal 来说,遇到会成环的边直接丢弃不劣;等价地,若为了连通性加入一条边导致出现环,那么把环里的一条更重边删掉,总权值不会变小,只会变小或相等”。证明(交换论证):取任意一条简单环 $C\subseteq E$,令 $e\in C$ 满足 $w(e)\ge w(x)$ 对所有 $x\in C$。若存在一棵最小生成树 $T$ 不含 $e$,结论对这条 $e$ 已证;若所有最小生成树都含 $e$,取其中一棵 $T$ 并考察 $C$。由于 $C$ 是环,$C\setminus\{e\}$ 在图中将 $e$ 的两个端点重新连通;把 $e$ 从 $T$ 中删去得到森林 $T-e$(两连通分量),环上必存在一条边 $f\in C\setminus\{e\}$ 横跨这两个分量(否则环无法闭合)。将 $f$ 加回去得 $T'=(T-e)\cup\{f\}$,它仍是生成树;并且 $w(T')=w(T)-w(e)+w(f)\le w(T)$。若 $w(f)<w(e)$,则 $w(T')<w(T)$,与 $T$ 的最小性矛盾;故只能有 $w(f)=w(e)$,此时 $T'$ 也是最小生成树,且不含 $e$。于是对任意环,其最重边总可以被剔除而不劣(权值严格劣或相等),这叫“环性质”。Kruskal 每次从未选边里取当前最小权值且不成环的边,若某条候选边在当下会与已选边构成环,则它属于某个环且不比环上别的边轻,按上面的结论它不必进入任何最优解,直接跳过不影响可达到的最小总权值。因此 Kruskal 的“成环就跳过”是安全且保持最优的;同理,从一棵树额外加一条边一定形成唯一环,若你坚持保留这条新增边,就必须从该环删去别的边以回到树结构,而为了使总权值不增加,理性选择是删去环中不轻于它的边(最重者),因此“出现环并作合理消解后,总权值不变或变小;若你误把较轻的删了、把较重的留下,总权值只会变大”。综上,生成树本身无环,而在构造过程中“成环即弃”的策略保证了总权值非增,最终得到最小生成树。

算法流程

  1. 读入 n 个点、m 条边,边是三元组 (u,v,w)

  2. w 升序排序所有边。

  3. 初始化并查集 DSU:parent[i]=isize[i]=1,已选边数 cnt=0,答案权值和 ans=0

  4. 依次扫描排序后的每条边 (u,v,w)

    • fu=find(u),fv=find(v)

    • fu!=fv:把这条边加入生成树:ans+=wunion(fu,fv)cnt++

    • fu==fv:跳过(会成环)。

  5. cnt==n-1 时停止;输出 ans 与边集。

  6. 若扫完仍 cnt<n-1,原图不连通,仅得到最小生成森林(或按题意报错/提示)。

这本质上是一个贪心的思想,非常易于理解

#include<bits/stdc++.h>
using namespace std;
struct E{int u,v,w;};
int fa[100005],s[100005];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool unite(int a,int b){
    a=find(a);b=find(b);
    if(a==b)return 0;
    if(s[a]<s[b])swap(a,b);
    fa[b]=a;s[a]+=s[b];
    return 1;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int n,m;cin>>n>>m;
    vector<E> e(m);
    for(int i=0;i<m;i++)cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e.begin(),e.end(),[](E a,E b){return a.w<b.w;});
    for(int i=1;i<=n;i++)fa[i]=i,s[i]=1;
    long long ans=0;int cnt=0;
    for(auto &x:e){
        if(unite(x.u,x.v)){
            ans+=x.w;
            if(++cnt==n-1)break;
        }
    }
    if(cnt<n-1)cout<<"No MST\n";
    else cout<<ans<<"\n";
}

Prim算法

核心思想

  1. 从任意一个起点开始,把它加入生成树集合 T

  2. 每次选择 T 内的点T 外的点 之间权值最小的边,把这条边和它连接的那个新点加进 T

  3. 重复这个过程,直到所有点都在 T 里。

执行流程

  • 选一个起点 → 入堆 (0, start)

  • 循环直到堆空:

    • 弹出堆顶 (w,u)

    • 如果 u 已访问 → 跳过

    • 否则加入 MST,累加权值

    • 遍历 u 的所有边 (u,v,w) → 如果 v 未访问 → 入堆 (w,v)

  • 直到所有点都加入 MST。

类似 Dijkstra

#include<bits/stdc++.h>
using namespace std;
struct E{int v,w;};
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int n,m;cin>>n>>m;
    vector<vector<E>> g(n+1);
    for(int i=0;i<m;i++){
        int u,v,w;cin>>u>>v>>w;
        g[u].push_back({v,w});
        g[v].push_back({u,w});
    }
    vector<int> vis(n+1,0);
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> pq;
    pq.push({0,1});
    long long ans=0;int cnt=0;
    while(!pq.empty()){
        auto [w,u]=pq.top();pq.pop();
        if(vis[u])continue;
        vis[u]=1;
        ans+=w;
        cnt++;
        for(auto &e:g[u])if(!vis[e.v])pq.push({e.w,e.v});
    }
    if(cnt==n)cout<<ans<<"\n";
    else cout<<"impossible\n";
}

总结(GPT-5生成)

特性

Prim 算法

Kruskal 算法

思路

从某个点开始,不断挑选最小的“跨界边”,扩展到新点(像染色扩散)

按边权排序,从小到大不断选边,只要不成环就加进树(像结婚撮合)

数据结构

邻接矩阵(O(n²))或 邻接表+堆(O(m log n))

边集+并查集,先排序 O(m log m)

复杂度

稠密图:O(n²)稀疏图:O(m log n)

O(m log m)

适合场景

稠密图(边接近 n²,Prim 更快)

稀疏图(边远小于 n²,Kruskal 更快)

边/点驱动

点驱动:从一个点扩展

边驱动:从小边开始合并

结果树

生成树与起点无关,但执行过程依赖起点

与起点无关,边的顺序全局固定

实现难度

稍复杂(需要堆/矩阵)

相对简单(排序+并查集)

直观比喻

像“传染病”从一个点向外扩散

像“婚姻介绍所”按边权撮合点对