思路来源
https://blog.csdn.net/DDelphine/article/details/77935670 详细图示
https://blog.csdn.net/u012317281/article/details/38115515 正确性证明
https://wenku.baidu.com/view/fdb484c3bb4cf7ec4afed08b.html 正确性证明
https://www.cnblogs.com/ylfdrib/archive/2010/08/17/1801784.html 板子整理
知识点整理
其实就是一个板子,不理解也罢,
但还是看了三个多小时来理解……
算法流程:
①随机选择一个点作为起始点,
类似最大生成树的计算过程,对每个不在树的距离进行更新,
这里是对wage数组进行更新,
即,对于不在树中的点u,要和当前生成树切断联系的代价为wage[u],
其实也就是,把u加入割点集所需消耗的代价,割点集两两不连通,
把代价大的割点取出来之后,剩下的就是代价小的,
每次选出最大的wage[u]加入树中,直到不在树中的点只剩两个s和t,
且如果下一步选点的话,应该选s,
说明t是wage[]值最小的,s是wage[]值次小的,
且由于t要切断与所有点的联系,所以wage[t]就是与t相连的边的条数
计算s和t的最小割,这是对于当前局面来讲,可割且最小的割
计算好答案之后,更新最小割的值,然后将s和t合并成一个点,
②重复①流程,至整个局面被合为一个点
由于每次类似prim的最大生成树的计算是,
而每次合并一个点,最多合并n-1次,故复杂度
算法正确性证明:
记最小割把点集分成A、B两个部分,
①若s和t分属不同的点集,则s和t的割就是最小割,
这种情况下,点集A和点集B都已经被缩成一个点,s和t一边一个
②若s和t分属相同的点集,则s和t的割不是最小割,
所以将s和t合并,并不会影响最小割,且会使一侧的点集缩小
但实际运行过程中,在最小割未确定前无法确定是①还是②,故将每次的答案都记录取最小
下证,s和t的分法只有①和②两种情况,
不会出现如下图的情况,s和t间的割不是最小割,且s和t合并之后最小割没了
设第一次随机选的点在A中,
记A中点第一次扩展到B中的点u时,使u和树不连通的代价是wage[u],
由于u只切断了和A树的联系,如上图,故wage[u]就是最小割
①若A中任意点v的wage[v]>wage[u],则会更新完A中所有点,再去更新B中点,s和t都在B中
②若A中存在点v满足wage[v]<wage[u],则由于B中除u外任意点wage>wage[u],更新完B中点再回到A中,s和t都在A中
③反证法,设最后满足A中一个点,B中一个点成立,则wage[s]和wage[t]都小于wage[u],
这表明只割掉t的方案会使最小割小于wage[u],与最小割矛盾,故不成立
所以就只剩两种情况啦,
其实抄板子就好了,说了这么多还是不会敲代码……
板子整理
以poj2914 Minimum Cut为例
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=505;
const int INF=0x7fffffff;
//a[x][y] 代表x到y连的边数 即割
//无向图 a[y][x]=a[x][y]
//点的编号 1-n
int n,m,u,v,w;
int a[N][N],dis[N];
bool vis[N],d[N];
int StoerWagner(int n)
{
int p=n,ans=INF,mx,t,s,k,i;
memset(d,0,sizeof d);
while (--p>0)
{
memset(vis,0,sizeof vis);
memset(dis,0,sizeof dis);
i=1;
while (d[i]) i++;
vis[i]=1;
for (int j=1;j<=n;j++)
if(!d[j] && !vis[j])
dis[j]=a[i][j];
t=s=i;
for (;i<=n;i++)
{
mx=0;
for (int j=1;j<=n;j++)
if (!d[j] && !vis[j] && mx<dis[j])
mx=dis[k=j];
if (!mx) break;
vis[k]=1;
for(int j=1;j<=n;j++)
if(!d[j] && !vis[j])
dis[j]+=a[k][j];
s=t;
t=k;
}
if (ans>dis[t]) ans=dis[t];
if (ans==0) return 0;
d[t]=1;
for (int j=1;j<=n;j++)
if (!d[j])
{
a[s][j]+=a[t][j];
a[j][s]+=a[j][t];
}
}
return ans;//返回无向图最小割
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(a,0,sizeof a);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&u,&v,&w);//输入是0到n-1的
u++;v++;
a[u][v]+=w;
a[v][u]+=w;
}
printf("%d\n",StoerWagner(n));
}
return 0;
}