Stoer-Wagner求无向图全局最小割


最小割Stoer-Wagner算法

 

割:在一个图GVE)中V是点集,E是边集。在E中去掉一个边集C使得GVE-C)不连通,C就是图GVE)的一个割;

最小割:GVE)的所有割中,边权总和最小的割就是最小割。

 

G的任意s-t最小割Min-Cst):

st是途中的两个点且边(st)∈E(即st之间存在一条边)。如果G的最小割CutG分成MN两个点集

①:如果sMtNMin-Cst= Cut(不讨论)

②:如果stM(或者stN)则Min-Cst<= Cut

 

我们来定义一个Contract(a,b)操作,即把ab两个点合并,表示为删除节点b,把b的节点信息添加到a

如下图是做了Contract5,6

最小割 <wbr>Stoer-Wagner <wbr>算法

最小割 <wbr>Stoer-Wagner <wbr>算法

对于所点vw(v,5)+=w(v,6)

 

s-t最小割的方法

定义w(A,x) = ∑w(v[i],x)v[i]∈A

定义Ax为在x前加入A的所有点的集合(不包括x

1.令集合A={a}aV中任意点

2.选取V-A中的w(A,x)最大的点x加入集合

3.|A|=|V|,结束,否则更新w(A,x),转到2

令倒数第二个加入A的点为s,最后一个加入A的点为t,则s-t最小割为w(At,t)

 

Poj (pku) 2914 Minimum Cut

的第三个case为例,图为

 

 

 

 

 

 

 

 

最小割 <wbr>Stoer-Wagner <wbr>算法

GVE

我们设法维护这样的一个w[],初始化为0

我们把V-A中的点中w[i]最大的点找出来加入A集合;

V-A直到为空

w[]的情况如下

w[i]

0

1

2

3

4

5

6

7

初始值

0

0

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

0

0

A=A{1}

 

---

2

2

1

0

0

0

A=A{2}

 

 

---

3

1

0

0

0

A=A{3}

 

 

 

---

1

0

0

1

A=A{4}

 

 

 

 

---

1

1

2

A=A{7}

 

 

 

 

 

2

2

---

A=A{5}

 

 

 

 

 

---

3

 

A=A{6}

 

 

 

 

 

 

---

 

 

每次w[i]+=(i,a)的权值aA

记录最后加入A的节点为t=6,倒数第二个加入A的为s=5,则s-t的最小割就为w[s],在图中体现出来的意思就是5-6的最小割为w[s]=3

然后我们做Contract(s,t)操作,得到下图

最小割 <wbr>Stoer-Wagner <wbr>算法

G(V’,E’)

重复上述操作

 

 

 

w[i]

0

1

2

3

4

5

7

初始值

0

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

0

A=A{1}

 

---

2

2

1

0

0

A=A{2}

 

 

---

3

1

0

0

A=A{3}

 

 

 

---

1

0

1

A=A{4}

 

 

 

 

---

2

2

A=A{5}

 

 

 

 

 

---

4

A=A{7}

 

 

 

 

 

 

---

s=5t=7    s-t最小割是4

Contract(s,t)得到

最小割 <wbr>Stoer-Wagner <wbr>算法

 

 

w[i]

0

1

2

3

4

5

初始值

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

A=A{1}

 

---

2

2

1

0

A=A{2}

 

 

---

3

1

0

A=A{3}

 

 

 

---

1

1

A=A{4}

 

 

 

 

---

4

A=A{5}

 

 

 

 

 

---

s=4t=5    s-t最小割是4

Contract(s,t)得到

最小割 <wbr>Stoer-Wagner <wbr>算法


 

 

w[i]

0

1

2

3

4

初始值

0

0

0

0

0

A=A{0}

---

1

1

1

1

A=A{1}

 

---

2

2

1

A=A{2}

 

 

---

3

1

A=A{3}

 

 

 

---

2

A=A{4}

 

 

 

 

---

s=3t=4    s-t最小割是2,(此时已经得出答案,以下省略)

 

AC代码:

 #include <iostream>

#include <stdio.h>

#include <string.h>

#include <queue>

 

#define INT_MAX 0x3f3f3f3f

 

using namespace std;

 

int mp[502][502];

int N,M;

bool combine[502];

int minC=INT_MAX;

 

void search(int &s,int &t){

    bool vis[502];

    int w[502];

    memset(vis,0,sizeof(vis));

    memset(w,0,sizeof(w));

    int tmpj=1000;

    for(int i=0;i<N;i++){

        int max=-INT_MAX;

        for(int j=0;j<N;j++){

            if(!vis[j]&&!combine[j]&&max<w[j]){

                max=w[j];

                tmpj=j;

            }

        }

        if(t==tmpj){minC=w[t];return;}

        vis[tmpj]=1;

        s=t,t=tmpj;

        for(int j=0;j<N;j++){

            if(!vis[j]&&!combine[j])

                w[j]+=mp[t][j];

        }

    }

    minC=w[t];

}

 

int mincut(){

    int ans=INT_MAX;

    int s,t;

    memset(combine,0,sizeof(combine));

    for(int i=0;i<N-1;i++){

        s=t=-1;

        search(s,t);

        combine[t]=true;

        ans=ans>minC?minC:ans;

        for(int j=0;j<N;j++){

            mp[s][j]+=mp[t][j];

            mp[j][s]+=mp[j][t];

        }

    }

    return ans;

}

 

int main(){

    //freopen("in.txt","r",stdin);

    while(cin>>N>>M){

        memset(mp,0,sizeof(mp));

        int u,v,w;

        while(M--){

            scanf("%d %d %d",&u,&v,&w);

            mp[u][v]+=w;

            mp[v][u]+=w;

        }

        cout<<mincut()<<endl;

    }

    return 0;

}


 

hdu3691Nubulsa Expo(Stoer-Wagner求无向图全局最小割)

分类: 图论   128人阅读  评论(0)  收藏  举报

题目请戳这里

题目大意:给一张图,n个点,m条无向边,每条边有权值,表示该路人流量上界。给定起点s,问如何选终点t,能是s-t的所有路径上最小人流量总和最大,给出这个最大流量。

题目分析:根据最大流最小割定理,此题就是求一个最小割。给定的起点是无用信息,因为起点一定在某个割集中,那么终点在另一个割集随便找一点即可。所以此题求的是一个全局最小割。最大流可以解决。但需要O(n)枚举终点。再加上最大流的复杂度,至少要O(n^4),对于此题来说复杂度偏高,所以要找其他算法。

Stoer-Wagner算法是求无向图全局最小割的一个有效算法,最坏时间复杂度O(n^3),主要思想是先找任意2点的最小割,然后记录下这个最小割,再合并这2个点。这样经过n-1次寻找任意2点最小割,每次更新全局最小割,最后整张图缩成一个点,算法结束,所保存下来的最小割就是全局最小割。

Stoer-Wagner的正确性:

设s和t是图G的2个顶点,图G的全局最小割要么是s-t的最小割,此时s和t在G的全局最小割的2个不同的子集中,或者是G中将s和t合并得的的新图G'的全局最小割,此时s和t在G的全局最小割的同一个子集中。所以只需要不断求出当前图中任意2个点的最小割,然后合并这2个点。不断缩小图的规模求得最小割。

关于更详细的Stoer-Wagner算法:

1:这是英文版论文,英语太烂,没勇气看,不过里面有个插图蛮好的,可以很直观的体会这个算法的工作过程。

2:这篇给了一点证明

3:看看吧

详情请见代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<algorithm>  
  5. using namespace std;  
  6. const int N = 305;  
  7. const int M = 50005;  
  8. const int inf = 0x3f3f3f3f;  
  9. int g[N][N],v[N],dis[N];  
  10. bool vis[N];  
  11. int m,n,s;  
  12. void build()  
  13. {  
  14.     int a,b,c;  
  15.     memset(g,0,sizeof(g));  
  16.     while(m --)  
  17.     {  
  18.         scanf("%d%d%d",&a,&b,&c);  
  19.         g[a][b] += c;  
  20.         g[b][a] += c;  
  21.     }  
  22. }  
  23. void solve()  
  24. {  
  25.     int i,j;  
  26.     int ans = inf;  
  27.     int maxx,maxi;  
  28.     int s,t;  
  29.     for(i = 1;i <= n;i ++)  
  30.         v[i] = i;//初始化点集  
  31.     while(n > 1)  
  32.     {  
  33.         int cur,pre;  
  34.         cur = 1;  
  35.         memset(dis,0,sizeof(dis));  
  36.         memset(vis,false,sizeof(vis));  
  37.         for(i = 2;i <= n;i ++)  
  38.         {  
  39.             dis[v[i]] = g[v[1]][v[i]];  
  40.         }  
  41.         vis[v[1]] = true;  
  42.         for(i = 1;i < n;i ++)  
  43.         {  
  44.             maxx = -1;  
  45.             maxi = 0;  
  46.             for(j = 1;j <= n;j ++)  
  47.             {  
  48.                 if(vis[v[j]] == false && maxx < dis[v[j]])  
  49.                 {  
  50.                     maxx = dis[v[j]];//找离当前集合最远的点  
  51.                     maxi = j;  
  52.                 }  
  53.             }  
  54.             vis[v[maxi]] = true;  
  55.             if(i == n - 2)  
  56.                 s = maxi;  
  57.             if(i == n - 1)  
  58.                 t = maxi;  
  59.             for(j = 1;j <= n;j ++)  
  60.             {  
  61.                 if(vis[v[j]] == false)  
  62.                     dis[v[j]] += g[v[maxi]][v[j]];  
  63.             }  
  64.         }  
  65.         ans = min(ans,dis[v[t]]);  
  66.         for(i = 1;i <= n;i ++)  
  67.         {  
  68.             g[v[s]][v[i]] += g[v[t]][v[i]];  
  69.             g[v[i]][v[s]] = g[v[s]][v[i]];  
  70.         }  
  71.         v[maxi] = v[n];  
  72.         n --;  
  73.     }  
  74.     printf("%d\n",ans);  
  75. }  
  76. int main()  
  77. {  
  78.     while(scanf("%d%d%d",&n,&m,&s),n)  
  79.     {  
  80.         build();  
  81.         solve();  
  82.     }  
  83.     return 0;  
  84. }  


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值