无向图最小割--poj2914

Stoer-Wagner算法

一个无向连通网络,去掉一个边集可以使其变成两个连通分量则这个边集就是割集;最小割集当然就权和最小的割集。
可以用最小切割最大流定理:
1.min=MAXINT,确定一个源点
2.枚举汇点
3.计算最大流,并确定当前源汇的最小割集,若比min小更新min
4.转到2直到枚举完毕
5.min即为所求输出min
不难看出复杂度很高:枚举汇点要O(n),最短增广路最大流算法求最大流是O((n^2)m)复杂度,在复杂网络中O(m)=O(n^2),算法总复杂度 就是O(n^5);哪怕采用最高标号预进流算法求最大流O((n^2)(m^0.5)),算法总复杂度也要O(n^4)
所以用网络流算法求解最小割集复杂度不会低于O(n^4)。
---------
prim算法不仅仅可以求最小生成树,也可以求“最大生成树”。最小割集Stoer-Wagner算法就是典型的应用实例。
求解最小割集普遍采用Stoer-Wagner算法,不提供此算法证明和代码,只提供算法思路:
1.min=MAXINT,固定一个顶点P
2.从点P用类似prim的s算法扩展出“最大生成树”,记录最后扩展的顶点和最后扩展的边
3.计算最后扩展到的顶点的切割值(即与此顶点相连的所有边权和),若比min小更新min
4.合并最后扩展的那条边的两个端点为一个顶点。
5.转到2,合并N-1次后结束
6.min即为所求,输出min
prim本身复杂度是O(n^2),合并n-1次,算法复杂度即为O(n^3)
如果在prim中加堆优化,复杂度会降为O((n^2)logn)  

算法没太看懂,先学着敲一下。

下面是代码(模板):

#include<iostream>
#include<set>
//#include<map>
#include<vector>
#include<queue>
#include<cmath>
#include<climits>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
const int MAX_N=505;

int N,M;
int mat[MAX_N][MAX_N];
int res;

void Stor()
{
    int node[MAX_N],dis[MAX_N];
    bool vis[MAX_N];
    int prev;
    for(int i=0;i<N;i++)
        node[i]=i;
    while(N>1)
    {
        int zj=1;
        for(int i=1;i<N;i++)//初始化到已圈集合的割大小
        {
            dis[node[i]]=mat[node[0]][node[i]];
            if(dis[node[i]]>dis[node[zj]])
                zj=i;
        }
        prev=0;
        memset(vis,0,sizeof(vis));
        vis[node[0]]=true;
        for(int i=1;i<N;i++)
        {
            if(i==N-1)//只剩最后一个没加入集合的点,更新最小割  
            {
                res=min(res,dis[node[zj]]);
                for (int k = 0; k <N; k++) //合并最后一个点以及推出它的集合中的点
                    mat[node[k]][node[prev]]=mat[node[prev]][node[k]]+= mat[node[k]][node[zj]];
                node[zj] = node[--N]; //缩点后的图
            }
            vis[node[zj]]=true;
            prev=zj;
            zj=-1;
            for(int j=1;j<N;j++)//将上次求的maxj加入集合,合并与它相邻的边到割集  
            {
                if(!vis[node[j]])
                {
                    dis[node[j]]+=mat[node[prev]][node[j]];
                    if(zj==-1||dis[node[zj]]<dis[node[j]])
                        zj=j;
                }

            }
        }
    }
    cout<<res<<endl;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int a,b,f;
    while(cin>>N>>M)
    {
        memset(mat,0,sizeof(mat));
        res=(1<<29);
        for(int i=0;i<M;i++)
        {
            cin>>a>>b>>f;
            mat[a][b]+=f;
            mat[b][a]+=f;
        }
        Stor();
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值