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

来源:互联网 发布:翔云软件 编辑:程序博客网 时间:2024/05/29 21:17

来自文章: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 505int 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;}


0 0
原创粉丝点击