全局最小割问题(Global Min-Cut Problem)是图论中的一个经典问题,旨在通过切割图中的边来划分图的顶点集合。具体来说,给定一个加权无向图 $ G = (V, E) $,图中每条边 $ e \in E $ 有一个权重 $ w(e) $,全局最小割问题的目标是找到一个划分 $ (S, T) $ 使得从集合 $ S $ 到集合 $ T $ 的边的总权重最小。也就是说,我们希望将图的顶点集合 $ V $ 分割成两个子集 $ S $ 和 $ T $,使得割集 $ \text{cut}(S) $ 的总权重最小,其中割集定义为从 $ S $ 到 $ T $ 的所有边的集合。
形式上,最小割问题的目标是求解以下优化问题:
\[\min_{S \subseteq V} \sum_{u \in S, v \in V \setminus S} w(u, v) \]
即要找到权重总和最小的一组边集,将无向图划分成两部分。
我们已经了解最大流最小割定理:在网络流图中,最大流等于最小割。这里的最小割和全局最小割的区别在于,最大流---最小割定理主要应用于流网络,其中有明确的源点(Source)和汇点(Sink)。在这种情况下,最小割是指将图划分成两部分,使得源点与汇点之间的流量最小,换句话说,它是指从源点到汇点的最小割集。而在全局最小割问题中,并不要求指定源点和汇点,而是要求找到任何两部分之间的最小割,不管这些部分是否包含特定的源汇结构。因此,全局最小割问题更加普适,适用于任何无向图,并且与特定的源点和汇点无关。
暴力求解
暴力方法的思路是,将无向边改为双向的有向边,首先在图中选取任意一个节点作为源点,然后枚举图中所有其他节点作为汇点。对于每一对源点和汇点,使用最大流算法来计算源点到汇点之间的最大流值,然后通过最大流---最小割定理来得到最小割的值。这个方法的缺点在于每次都需要执行最大流算法,非常低效。
Stoer--Wagner 算法
Stoer--Wagner 算法利用无向图和最小割的性质,使用最大邻接序和递归来求解全局最小割。这个算法基于这样的前提:图中任取两点 \(s\) 和 \(t\),它们要么位于我们要找的全局最小割的一边,要么位于两边
这里,我们并不枚举 \(s\) 和 \(t\) ,因为我们不知道最终的最小割是如何划分的,但根据图的结构,我们能够确定 \(s\) 和 \(t\) 一定是全局最小割中的两个端点之一,然后根据上面的两种情况分治。
最大邻接排序求解 s-t 最小割
我们需要处理两种情况之一:要么 \(s\) 和 \(t\) 位于最小割的同一边,要么它们位于割的两边。对于后一种情况,也就是 \(s\) 和 \(t\) 位于两边的情况,我们使用一个称为 Maximum Adjacency Ordering 的方法来处理。
Maximum Adjacency Ordering 也被称为"最大邻接排序",它的基本思路是:
- 从任意一个顶点出发,将其加入"已选节点"。
- 每轮选择非"已选节点"里,连接到"已选节点"的边权之和最大的节点。
- 通过这种方式,我们可以得到一个顶点的排列顺序。这个顺序确保了在每次选择时,我们总是优先选择连接当前已选择顶点的边权重最大的顶点。
具体而言,假设我们从任意的起点开始,然后按照"最大邻接边"的顺序遍历图,最终得到一个顶点的排序序列。最后的两个顶点(排序序列中的最后两个节点)就成为了图的 s-t割 中的两个端点。
也可以视为,我们从初始节点视作"大节点",每次选择"大节点"邻边里权重最大者并入(将其边权附加过来),直到剩下两个点,最后一条边的边权就是最后两个点构成的 s-t割

可以用归纳法证明此贪心策略的正确。每一次合并都不改变图的最小割值,最终剩下的两个点之间的边就是这两点间的一个割,最大邻接排序可以得到正确的结果。
单次时间复杂度
这种贪心------取最大的策略和 prim 或 dijkstra 相同,其复杂度为\(O(V^2)\)(暴力遍历节点)、\(O((V+E) \log V)\)(二叉堆)或 \(O(E+V \log V)\)(斐波那契堆)
合并两个顶点并递归
对于 \(s\) 和 \(t\) 位于同一侧的情况,我们就可以将这两个顶点合并为一个新顶点(将其邻边合并,连接到同一点的边权被相加),并进入下一轮的算法。
为什么合并是对的?
在 Stoer-Wagner 算法中,每次迭代时通过合并顶点来简化问题。这是因为,在最大邻接排序中,顶点 \(s\) 和 \(t\) 已经被识别为割的两端点,接下来考虑的是 \(s\) 和 \(t\) 在同一侧的情况。合并这两个顶点不会改变这种情况下最小割的性质。换句话说,合并顶点相当于"压缩"图的结构,但不会影响最小割的最终结果。因为合并后,新的顶点将保留 \(s\) 和 \(t\) 之间的所有连接信息,且这些连接信息在下一轮计算时依然能帮助我们找到最小割。
合并操作具体来说,就是将 \(s\) 和 \(t\) 合并成一个新的虚拟顶点,并将与这两个顶点相连的所有边重新连接到新的虚拟顶点上。在合并后,剩下的图的规模会减少,新的图中就只剩下 \(V-1\) 个顶点。
然后,算法进入下一轮,继续寻找下一个可能的最小割,直到剩下两个顶点为止。

cpp
// Stoer-Wagner 暴力版
// 参考题目:https://www.luogu.com.cn/problem/P5632
class Graph {
vector<vector<int>> adj; // 邻接矩阵
int n;
public:
Graph(int n) : n(n), adj(n, vector<int>(n, 0)) {}
void addEdge(int u, int v, int w) {
adj[u][v] = adj[v][u] = w;
}
int stoerWagner() {
int res = INT_MAX;
for (int i = 0; i < n - 1; ++i) {
vector<int> ma(n, 0);
ma[0] = INT_MAX; // 选择总是从 0 开始
int s = -1, t = -1;
for (int j = 0; j < n - i - 1; ++j) {
int a = max_element(ma.begin(), ma.end()) - ma.begin();
if (ma[a] == 0) return 0; // Graph is disconnected
ma[a] = -1;
if (j == n - i - 2) s = a; // The second last node is s
for (int k = 0; k < n; ++k) {
if (ma[k] >= 0) ma[k] += adj[a][k];
}
}
t = max_element(ma.begin(), ma.end()) - ma.begin();
res = min(res, ma[t]);
// Merge nodes s and t
for (int k = 0; k < n; ++k) {
if (k != s && k != t) {
adj[s][k] += adj[t][k];
adj[k][s] = adj[s][k];
adj[t][k] = adj[k][t] = 0;
}
}
} return res;
}
};
总复杂度
Stoer-Wagner 算法的时间复杂度由以下几部分组成:
算法会执行 \(V-1\) 轮迭代,因为每次迭代都会减少一个顶点,直到最终剩下两个顶点。
与单次时间复杂度相乘,整个算法的时间复杂度近似为 \(O(V^3)\)。
Stoer-Wagner 算法通过贪心策略和顶点合并大大降低了时间复杂度,用于解决无源点和汇点的无向图最小割,比暴力方法每次运行最大流算法要高效得多。当然算法是专门为最小割问题设计的,它可能不如最大流算法那样在其他流网络问题中具备广泛的适用性。