Stoer-Wagner

Stoer-Wagner 简介

  • 无向正权图上全局最小割 \(O(nm)\) 大概?

流程

  • 基本原理,我们设答案割掉的边集为 \(c\),那么对于任意段对,\((s,t)\) 要么 \(c\)\(s,t\) 的最小割,要么 \(s,t\) 在割完之后同属一个联通快。
  • 于是我们自然地可以找两个点对 \(a,b\) 算其最小割,此时我们就覆盖了所有会是 \(a,b\) 最小割的答案。只剩下 \(a,b\) 同属一个联通快的情况,不妨将 \(a,b\) 合并为 \(1\) 点,继续考虑,直到原图只剩下一个点。
  • 采用 Dinic ,复杂度就是 \(O(mn^3)\) 有点慢,能不能快点!于是我们有了 Stoer-Wagner
  • 这里,采用一种更快的方式,求最小割。
  • 具体的,考虑我们只需要给出任意给出两点和其最小割就行。
  • 我们维护集合 \(A\),初始 \(A=\phi\),每次找到不在集合 \(A\) 中的代价最大的点 \(x\),代价为 \(w(A,x)=\sum_{i\in A}d(i,x)\) 其中 \(d(i,x)\) 表示边权,没有边则为 \(0\)
  • 容易得到,对于同一个图,\(A\) 中点的加入顺序是固定的。我们说我们找到了一组最小割,两点分别为,最后一次加入 \(A\) 的点 \(t\),和倒数第二次加入 \(A\) 的点 \(s\)。我们说最小割就是 \(w(V/\{s\},t)\) 就是最后加入 \(t\) 时候的代价。
  • 接下来我们来证明 \(s,t\) 的最小割就是它。
  • 我们先定义一些东西,方便我们描述
  • \(A_u\) 表示 \(u\) 加入 \(A\) 之前 \(A\) 集合里的元素、
  • 首先有 \(C_{min}(s,t)\le w(A_t,t)\) 这是为什么呢?我们把与 \(t\) 有关的边都删了,那一定是一个割,于是,最小割小于等于它。
  • 接下来只需证 \(w(A_t,t)\le C_{min}(s,t)\) 就可以说明 \(w(A_t,t)=C_{min}(s,t)\) 了。
  • 我们提取我们的最小割 \(C_{min}(s,t)\) 的边集。让 \(C(A_x)\) 表示在 \(A_x\) 的生成子图中,边集和 \(C_{min}(s,t)\) 的边集交的部分。
  • 我们再来找到一些关键点 \(u\),满足,在 \(u\) 之前一个加入 \(A\) 的点 \(v\)。使得 \(C(A_u+\{u\})\)\(u,v\) 的割。
  • 我们找到所有的关键点,按照加入 \(A\) 的顺序,排列成 \(p_i\)
  • \(p_1\) 显然满足, \(w(A_{p_1},p_1)=C(A_{p_1}+\{p_1\})\)
  • 我们考虑归纳证明 \(w(A_{p_i},p_i)\le C(A_{p_i}+\{p_i\})\)
  • 于是我们有 \(w(A_{p_{i+1}},p_{i+1})=w(A_{p_i},p_{i+1})+w(A_{p_{i+1}}-A_{p_i},p_{i+1})\)
  • 由于 \(w(A_{p_{i}},p_{i+1})\le w(A_{p_{i}},p_{i})\le C(A_{p_i}+\{p_i\})\) 第一步是因为我们每次选的都是代价最大的
  • 整理一下 \(w(A_{p_{i+1}},p_{i+1})\le C(A_{p_i}+\{p_i\})+w(A_{p_{i+1}}-A_{p_i},p_{i+1})\le C(A_{p_{i+1}}+\{p_i+1\})\)
  • 最后那个小于等于是因为,\(w\) 一定是割掉的一部分。
  • 于是得证.

【模板】Stoer-Wagner

#include <cstring>
#include <iostream>
using namespace std;
const int N = 660;
int G[N][N], can[N], w[N], vis[N], n, m;
int GetC(int& s, int& t)
{
    memset(w, 0, sizeof w);
    memset(vis, 0, sizeof vis);
    w[0] = -1; s = -1; t = -1; int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        int ma = 0;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && can[j] && w[ma] < w[j])ma = j;
        if (!ma) break;
        s = t; t = ma; ans = w[ma]; vis[ma] = 1;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && can[j])w[j] += G[ma][j];
    }
    return ans;
}
int SW()
{
    int ans = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)can[i] = 1;
    while (1)
    {
        int s, t, w = GetC(s, t);
        if (s == -1)break; can[t] = 0;
        for (int i = 1; i <= n; i++) if(can[i])
        {
            G[s][i] += G[t][i];
            G[i][s] += G[i][t];
        }
        ans = min(ans, w);
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        G[x][y] += z; G[y][x] += z;
    }
    cout << SW() << '\n';
    return 0;
}
posted @ 2025-04-23 08:01  LUHCUH  阅读(20)  评论(0)    收藏  举报