poj 2914 全局最小割(stoer_wagner算法)

介绍了一个简单的最小割算法,该算法基于一个关键定理,通过Contract操作不断缩小图规模直至找到全局最小割。提供了完整的算法流程及求解任意s-t最小割的方法,并附带实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来自文章:A Simple Min-Cut Algorithm。作者:MECHTHILD STOER,FRANK WAGNER。(看了一遍原文,想记录下来,看到http://www.cnblogs.com/ylfdrib/archive/2010/08/17/1801784.html 上有简介就摘过来了)

算法基于这样一个定理:对于任意s, t ∈ V  ,全局最小割或者等于原图的s-t 最小割,或者等于将原图进行 Contract(s, t)操作所得的图的全局最小割(显然的,将s、t文类讨论,属于最小割的两部还是其中一部)。
假设不存在边(p, q),则定义边(p, q)权值w(p, q) = 0 。
Contract 操作定义: Contract(a, b): 删掉点 a, b 及边(a, b),加入新节点 c,对于任意 v∈V  ,w(v, c) = w(c, v) = w(a, v) + w(b, v) 


算法框架:
1. 设当前找到的最小割MinCut 为+∞
2. 在 G中求出任意 s-t 最小割 c(方法见下文),MinCut = min(MinCut, c)
3. 对 G作 Contract(s, t)操作,得到 G'=(V', E'),若|V'| > 1,则G=G'并转 2,否则MinCut 为原图的全局最小割。 

求 G=(V, E)中任意 s-t 最小割的算法:
定义w(A, x) = ∑w(v[i], x),v[i]∈A,x∉A。  
定义 Ax 为在x 前加入 A 的所有点的集合(不包括 x)
1. 令集合 A={a},a为 V中任意点
2. 选取 V - A中的 w(A, x)最大的点 x加入集合 A
3. 若|A|=|V|,结束
令倒数第二个加入 A的点为 s,最后一个加入 A的点为 t,则s-t 最小割为 w(At, t)(即t为一部,剩下的为另一部)。用归纳法证明,不难看懂。


2914题意:裸的全局最小割,两点之间可能有多条边,那么边数就当做权值即可。

思路:上述算法(未加堆优化,时间复杂度O(n^3))。

#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s));
#define N 505
int g[N][N],n,m;
int del[N],used[N],dis[N];
int test(int num){
    int i,j,s,t,now,mm;
    clr(used, 0);
    for(i = 1;i<n;i++)
        if(!del[i])
            dis[i] = g[0][i];
    for(i = 1,t = 0;i<num;i++){
        mm = 0;
        for(j = 1;j<n;j++){//找新加入的点,即和已加入点集里面点的路径和最大的点
            if(!del[j] && !used[j] && dis[j]>mm){
                mm = dis[j];
                now = j;
            }
        }
        if(mm==0)//说明找不到,不连通,那么连通度必然为0
            return 0;
        used[now] = 1;
        s = t;
        t = now;
        for(j = 1;j<n;j++)//更新距离
            if(!del[j] && !used[j])
                dis[j] += g[now][j];
    }
    del[t] = 1;//缩点,将t缩到s
    for(i = 0;i<n;i++)
        if(!del[i] && i!=s)
            g[i][s] = (g[s][i] += g[i][t]);
    return dis[t];
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        int i,a,b,c,res=INF;
        clr(g, 0);
        clr(del, 0);
        for(i = 1;i<=m;i++){
            scanf("%d %d %d",&a,&b,&c);
            g[a][b] += c;
            g[b][a] += c;
        }
        for(i = 0;i<n-1;i++){
            res = min(res,test(n-i));
            if(!res)
                break;
        }
        printf("%d\n",res);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值