[学习笔记]全局最小割StoerWagner算法
概念
无向图的割:删除割集中的边可以使无向图不联通。
\(ST\)割:使得\(S\)与\(T\)不联通的割称做\(ST\)割。
\(ST\)最小割:\(ST\)割中边权和最小的方案。
全局最小割:边权和最小的割。
诱导割:一个割在子图中所存在的部分,称做诱导割,其类似于诱导子图概念。
算法流程:
伪代码
def MinimumCutPhase(G, w, a):
A ← {a}
while A ≠ V:
把与A联系最紧密(most tightly)的顶点加入A中
cut-of-the-phase ← w(A \ t, t)
合并最后两个加入到A的顶点s、t
return cut-of-the-phase
def StoerWagner(G, w, a):
while |V| > 1
MinimumCutPhase(G, w, a)
根据返回值更新最小割
其中\(w(A,v)\)为\(v\)到集合\(A\)中的点的所有边权之和
最紧密的指:\(\max(w(A,x))\)的\(x\)
证明:
最小割,要么是\(s-t\)割,要么是非\(s-t\)割,后者将两点合并不影响。
考虑证明我们\(MinimumCutPhase\)找出来的\(s−t\)割\(cut-of-the-phase\)为什么是最小的。
称\(A_u\)为加入\(u\)前的\(A\)集合。
\(C\)为任意\(s-t\)割,\(C_u\)为\(A_u + {u}\)部分的诱导割。
称\(u\)为活跃的当\(A_u\)的最后一个点与\(u\)在\(C\)中分属两边。
考虑归纳证明。
第一个活跃节点显然满足条件。
设\(v\)满足条件,\(u\)为下一个活跃节点。
\(w(A_u,u) = w(A_v,u) + w(A_u - A_v,u) = \alpha\)
有\(w(A_v,u) \leq w(A_v,v) \leq w(C_v)\)
有\(w(C_v) + w(A_u - A_v,u) \leq w(C_u)\)
两式联立可得原式。
全局最小割StoerWagner算法
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int MAXN = 3100;
LL mat[MAXN][MAXN];
LL weight[MAXN];
bool del[MAXN], vis[MAXN];;
int n, m, st;
void init() {
memset(mat, 0, sizeof(mat));
memset(del, 0, sizeof(del));
}
LL StoerWagner(int &s, int &t, int cnt) {
memset(weight, 0, sizeof(weight));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i)
if(!del[i]) {t = i; break; }
while(--cnt) {
vis[s = t] = true;
for(int i = 1; i <= n; ++i) if(!del[i] && !vis[i]) {
weight[i] += mat[s][i];
}
t = 0;
for(int i = 1; i <= n; ++i) if(!del[i] && !vis[i]) {
if(weight[i] >= weight[t]) t = i;
}
}
return weight[t];
}
void merge(int s, int t) {
for(int i = 1; i <= n; ++i) {
mat[s][i] += mat[t][i];
mat[i][s] += mat[i][t];
}
del[t] = true;
}
LL solve() {
LL ret = -1;
int s, t;
for(int i = n; i > 1; --i) {
if(ret == -1) ret = StoerWagner(s, t, i);
else ret = min(ret, StoerWagner(s, t, i));
merge(s, t);
}
return ret;
}
int main() {
while(scanf("%d%d", &n, &m) != EOF) {
if(n == 0 && m == 0) break;
init();
while(m--) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
mat[x][y] += z;
mat[y][x] += z;
}
cout<<solve()<<endl;
}
}