zoukankan      html  css  js  c++  java
  • 全局最小割 学习总结

    全局最小割的意思是在一个无向图中任取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
    ",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
    ",Get_SW());
        }return 0;
    }
    

      

  • 相关阅读:
    double 和 int 同时存在时的运算
    快速排序
    案例:商品放大镜效果
    淘宝flexible.js源码分析
    案例:模态框拖拽
    Web APIs——BOM
    案例:获取URL参数数据
    案例:5秒之后自动跳转页面
    JS中this指针的指向
    按钮:点击发送短信按钮60秒内不能再次点击的功能
  • 原文地址:https://www.cnblogs.com/joyouth/p/5648681.html
Copyright © 2011-2022 走看看