[学习笔记]全局最小割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;
    }
}
posted @ 2022-01-07 15:45  fhq_treap  阅读(112)  评论(0编辑  收藏  举报