灵魂滚烫, 命运冰凉|

fhq_treap

园龄:5年7个月粉丝:66关注:21

[学习笔记]全局最小割StoerWagner算法

概念

无向图的割:删除割集中的边可以使无向图不联通。
ST割:使得ST不联通的割称做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

证明:

最小割,要么是st割,要么是非st割,后者将两点合并不影响。

考虑证明我们MinimumCutPhase找出来的stcutofthephase为什么是最小的。

Au为加入u前的A集合。

C为任意st割,CuAu+u部分的诱导割。

u为活跃的当Au的最后一个点与uC中分属两边。

考虑归纳证明。

第一个活跃节点显然满足条件。

v满足条件,u为下一个活跃节点。

w(Au,u)=w(Av,u)+w(AuAv,u)=α

w(Av,u)w(Av,v)w(Cv)
w(Cv)+w(AuAv,u)w(Cu)

两式联立可得原式。

全局最小割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;
    }
}

本文作者:fhq_treap

本文链接:https://www.cnblogs.com/dixiao/p/15775362.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   fhq_treap  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2021-01-07 [POI2002][HAOI2007]反素数
2021-01-07 [SDOI2012] Longge 的问题
2021-01-07 [TJOI2007] 可爱的质数
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起