全局最小割 学习总结

全局最小割的意思是在一个无向图中任取S和T,求最小割的最小值

还有一种描述是删掉无向图中的边使得其不连通的最小代价

当然,这种题目可以用分治+最小割来求解

但是时间复杂度大约在O(n^4)左右

有一种更好的求解方法可以在O(n^3)的时间复杂度内求解

做法是这样的:

首先对于图中任意两点S->T

要么S和T不在一个集合里时是答案,答案显然是S和T的最小割

否则S和T在一个集合里,我们可以将S和T缩成一个点,不难证明这样是等效的

我们模拟这个过程,每次任取S和T跑最小割,时间复杂度大概跟分治+最小割差不多OwO

但是我们注意到这一步的S和T都是任取的,也就是我们只需要求出一组解就可以了

这样就有了一种构造算法:

1、一开始A集合为空,我们任取一点加入A集合中

2、定义W(A,x)表示A集合所有点到x的边权和

3、每次寻找W(A,x)最大的点(相同的话任取),加入A集合中并更新其他的W值

4、最后加入的两个点则分别是S和T,其最小割为最后加入时W(A,T)

这个过程直接模拟显然是O(n^2)的,加上缩点最多进行O(n)次

所以总时间复杂度O(n^3),可以用堆优化,但是稠密图跑得很慢很慢

 

BZOJ 3345

 
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm> 
using namespace std;
 
const int maxn=510;
const int oo=0x7fffffff;
int n,m,u,v,d;
int S,T,cut;
int f[maxn][maxn];
int w[maxn];
bool com[maxn],vis[maxn];
 
void Get_ans(){
    cut=oo;S=T=-1;
    memset(vis,false,sizeof(vis));
    memset(w,0,sizeof(w));
    for(int i=1;i<=n;++i){
        int mx=-oo,tmp;
        for(int j=1;j<=n;++j){
            if(!com[j]&&!vis[j]&&w[j]>mx){
                mx=w[j];tmp=j;
            }
        }
        if(T==tmp)return;
        S=T;T=tmp;
        cut=mx;vis[tmp]=true;
        for(int j=1;j<=n;++j)if(!com[j]&&!vis[j])w[j]+=f[tmp][j];
    }return;
}
int Get_SW(){
    memset(com,false,sizeof(com));
    int ans=oo;
    for(int i=1;i<n;++i){
        Get_ans();
        ans=min(ans,cut);
        if(ans==0)return 0;
        com[T]=true;
        for(int j=1;j<=n;++j){
            if(!com[j]){f[S][j]+=f[T][j];f[j][S]+=f[j][T];}
        }
    }return ans;
}
 
int main(){
    scanf("%d%d",&n,&m);
    memset(f,0,sizeof(f));
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&u,&v,&d);
        f[u][v]+=d;f[v][u]+=d;
    }
    printf("%d\n",Get_SW());
    return 0;
}

hdu 3691

注意到这里的S是给定的

但是不难发现这并没有什么卵用,答案还是全局最小割

因为设最后全局最小割中一定有一个T跟S不属于一个集合OwO

又因为是全局最小割,所以这组S->T的最小割就是全局最小割

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int maxn=310;
const LL oo=1LL<<60;
int n,m,k;
int u,v,w;
int S,T;
LL ans,cut;
LL f[maxn][maxn];
LL W[maxn];
bool com[maxn],vis[maxn];

void read(int &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void Get_ans(){
    cut=oo;S=T=-1;
    memset(vis,false,sizeof(vis));
    memset(W,0,sizeof(W));
    for(int i=1;i<=n;++i){
        LL mx=-oo;int tmp;
        for(int j=1;j<=n;++j){
            if(!com[j]&&!vis[j]&&W[j]>mx){mx=W[j];tmp=j;}
        }
        if(T==tmp)return;
        S=T;T=tmp;
        cut=mx;vis[tmp]=true;
        for(int j=1;j<=n;++j){
            if(!com[j]&&!vis[j]){W[j]+=f[tmp][j];}
        }
    }return;
}
LL Get_SW(){
    ans=oo;memset(com,0,sizeof(com));
    for(int i=1;i<n;++i){
        Get_ans();
        ans=min(ans,cut);
        if(ans==0)return ans;
        com[T]=true;
        for(int j=1;j<=n;++j){
            if(!com[j]){f[S][j]+=f[T][j];f[j][S]+=f[j][T];}
        }
    }return ans;
}

int main(){
    while(scanf("%d%d%d",&n,&m,&k)==3){
        if(!n&&!m&&!k)break;
        memset(f,0,sizeof(f));
        for(int i=1;i<=m;++i){
            read(u);read(v);read(w);
            f[u][v]+=w;f[v][u]+=w;
        }
        printf("%d\n",Get_SW());
    }return 0;
}

  

posted @ 2016-07-07 07:26  _Vertical  阅读(672)  评论(0编辑  收藏  举报