最小割 Stoer-Wagner 算法

轉自:夜晚的蟲子 http://blog.sina.com.cn/s/blog_700906660100v7vb.html

割:在一個圖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;
}


發佈了437 篇原創文章 · 獲贊 20 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章