最小割Stoer-Wagner算法
割:在一个图G(V,E)中V是点集,E是边集。在E中去掉一个边集C使得G(V,E-C)不连通,C就是图G(V,E)的一个割;
最小割:在G(V,E)的所有割中,边权总和最小的割就是最小割。
求G的任意s-t最小割Min-C(s,t):
设s,t是途中的两个点且边(s,t)∈E(即s,t之间存在一条边)。如果G的最小割Cut把G分成M,N两个点集
①:如果s∈M,t∈N则Min-C(s,t)= Cut(不讨论)
②:如果s,t∈M(或者s,t∈N)则Min-C(s,t)<= Cut
我们来定义一个Contract(a,b)操作,即把a,b两个点合并,表示为删除节点b,把b的节点信息添加到a上
如下图是做了Contract(5,6)
对于所点v有w(v,5)+=w(v,6)
求s-t最小割的方法
定义w(A,x) = ∑w(v[i],x),v[i]∈A
定义Ax为在x前加入A的所有点的集合(不包括x)
1.令集合A={a},a为V中任意点
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为例,图为

G(V,E)
我们设法维护这样的一个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)的权值a∈A
记录最后加入A的节点为t=6,倒数第二个加入A的为s=5,则s-t的最小割就为w[s],在图中体现出来的意思就是5-6的最小割为w[s]=3
然后我们做Contract(s,t)操作,得到下图

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=5,t=7
Contract(s,t)得到
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=4,t=5
Contract(s,t)得到
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=3,t=4
AC代码:
#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){
}
int mincut(){
}
int main(){
}
题目大意:给一张图,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:看看吧
详情请见代码:
- #include <iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- using namespace std;
- const int N = 305;
- const int M = 50005;
- const int inf = 0x3f3f3f3f;
- int g[N][N],v[N],dis[N];
- bool vis[N];
- int m,n,s;
- void build()
- {
- int a,b,c;
- memset(g,0,sizeof(g));
- while(m --)
- {
- scanf("%d%d%d",&a,&b,&c);
- g[a][b] += c;
- g[b][a] += c;
- }
- }
- void solve()
- {
- int i,j;
- int ans = inf;
- int maxx,maxi;
- int s,t;
- for(i = 1;i <= n;i ++)
- v[i] = i;//初始化点集
- while(n > 1)
- {
- int cur,pre;
- cur = 1;
- memset(dis,0,sizeof(dis));
- memset(vis,false,sizeof(vis));
- for(i = 2;i <= n;i ++)
- {
- dis[v[i]] = g[v[1]][v[i]];
- }
- vis[v[1]] = true;
- for(i = 1;i < n;i ++)
- {
- maxx = -1;
- maxi = 0;
- for(j = 1;j <= n;j ++)
- {
- if(vis[v[j]] == false && maxx < dis[v[j]])
- {
- maxx = dis[v[j]];//找离当前集合最远的点
- maxi = j;
- }
- }
- vis[v[maxi]] = true;
- if(i == n - 2)
- s = maxi;
- if(i == n - 1)
- t = maxi;
- for(j = 1;j <= n;j ++)
- {
- if(vis[v[j]] == false)
- dis[v[j]] += g[v[maxi]][v[j]];
- }
- }
- ans = min(ans,dis[v[t]]);
- for(i = 1;i <= n;i ++)
- {
- g[v[s]][v[i]] += g[v[t]][v[i]];
- g[v[i]][v[s]] = g[v[s]][v[i]];
- }
- v[maxi] = v[n];
- n --;
- }
- printf("%d\n",ans);
- }
- int main()
- {
- while(scanf("%d%d%d",&n,&m,&s),n)
- {
- build();
- solve();
- }
- return 0;
- }