题目链接:http://poj.org/problem?id=2914
一种求最小割的方法是Stoer-Wagner算法。它的核心在于这样一条定理:对于任意点s,t,无向图的最小割等于s,t的最小割,或是将s,t合并后的最小割。
//将s,t合并,指建立新点u,对于任意点v,w(u,v)=w(s,v)+w(t,v)
基于prim算法,我们得到这样的算法求任意s,t最小割(这里的任意是指,算法会给出随意一对s,t的最小割,而不是它可以求给定的s,t最小割):
//w(p)表示从点集A到不在A中的点p的所有边权之和。
arbitrary-mincut()
set A to a empty set
while A!=V(G)
find vertex p: max(w(p)),p is not in A
update w using p
add p to A
s,t <- the last two nodes added to A
return (s,t,w(t))
我们令s,t表示最后加入A集的两个点,则算法求出了从s到t的最小割。
根据上面的定理,整个图的最小割要么等于w(t),要么等于合并后的最小割。所以用w(t)更新答案,然后合并。
整个算法是这样的:
Stoer-Wagner()
cut <- infinity
while (|V|>1)
(s,t,w)= arbitrary-mincut()
cut <- min(cut,w)
merge s & t
return cut
Code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define M 512
#define Inf 0x3fffff
int s[M][M];
int vis[M],en[M];
int w[M];
int n,m;
int main()
{
int i,j,k,max;
int u,v,c,f,e;
int ans,t=1;
while(~scanf("%d%d",&n,&m)){
memset(s,0,sizeof(s));
while(m--){
scanf("%d%d%d",&u,&v,&c);
s[u][v]+=c,s[v][u]+=c;
}
memset(vis,-1,sizeof(vis));
ans=Inf;
for(k=1;k<n;k++){
memset(w,0,sizeof(w));
for(i=0;i<n;i++){
max=-1;c=-1;
for(j=0;j<n;j++){
if(en[j]!=t&&vis[j]!=k&&w[j]>max)
max=w[j],c=j;
}
if(c==-1) break;
vis[c]=k;
f=e;e=c;
for(j=0;j<n;j++){
if(vis[j]!=k)
w[j]+=s[c][j];
}
}
if(w[e]<ans) ans=w[e];
en[f]=en[e]=t;
for(j=0;j<n;j++){
if(en[j]!=t)
s[j][f]+=s[j][e],s[f][j]+=s[e][j];
}en[f]=0;
}
printf("%d\n",ans==Inf?0:ans);t++;
}
return 0;
}