zoukankan      html  css  js  c++  java
  • 全局最小割 stoer-wagner与karger-stein算法

    stoer-wagner算法

    进行n轮操作,每轮操作确定一对点s,t被割开情况下的最小割,然后将s,t合并。s,t为操作中最后剩下的两个点。

    操作类似prim求最大生成树,每次将与当前集合相邻的距离最大的点合并到集合中,最后剩下s,t两点。

    代码来自wiki

    const int maxn = 550;
    const int inf = 1000000000;
    int n, r;
    int edge[maxn][maxn], dist[maxn];
    bool vis[maxn], bin[maxn];
    void init() {
        memset(edge, 0, sizeof(edge));
        memset(bin, false, sizeof(bin));
    }
    int contract( int &s, int &t ) {        // Find s,t
        memset(dist, 0, sizeof(dist));
        memset(vis, false, sizeof(vis));
        int i, j, k, mincut, maxc;
        for(i = 1; i <= n; i++) {
            k = -1;
            maxc = -1;
            for(j = 1; j <= n; j++)if(!bin[j] && !vis[j] && dist[j] > maxc) {
                    k = j;
                    maxc = dist[j];
                }
            if(k == -1)return mincut;
            s = t;
            t = k;
            mincut = maxc;
            vis[k] = true;
            for(j = 1; j <= n; j++)if(!bin[j] && !vis[j])
                    dist[j] += edge[k][j];
        }
        return mincut;
    }
    
    int Stoer_Wagner() {
        int mincut, i, j, s, t, ans;
        for(mincut = inf, i = 1; i < n; i++) {
            ans = contract( s, t );
            bin[t] = true;
            if(mincut > ans)mincut = ans;
            if(mincut == 0)return 0;
            for(j = 1; j <= n; j++)if(!bin[j])
                    edge[s][j] = (edge[j][s] += edge[j][t]);
        }
        return mincut;
    }

    --------------------------------------------------------------------------------------------------------

     对于特殊的所有边权值都为1的全局最小割,存在复杂度更低但常数较大的karger-stein算法

    考虑store-wagner的缩边的过程。如果随机取边,正确性会怎样呢?

    假设最小割为k,则边数 。选中最小割中任意一条边的概率

    将这条边缩去重复直到只剩两个点,每一步都不选中最小割中边的概率为

    这样我们重复n2logn次失败的概率,此处使用了e的定义式

    于是我们得到了在n^2mlogn时间复杂度下,1/n正确率的算法

    在进行这个过程时每当图的点数变为原图的1/√2时,记录当前状态后进行两次对该状态的求解

    这个做法的正确概率为

    时间复杂度为

    重复log2n次,我们就可以得到在n^2log^3n的复杂度下达到1./n正确性的算法

     在实际测试中的表现

          10次Karger-stein     stoer-wagner

     n=2000       40s       10s

     n=5000       340s      134s

    但是在这种情况下Karger-stein的正确率都大于1/2,有一定的优越性。

    另外,所有最小割在Karger-stein运行过程中未被找到的数量的期望是1/n,几乎可以找到所有合法的最小割。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #define ULL unsigned long long 
    #define LDB long double
    using namespace std;
    
      ULL sed=233;
      int top,deg[5010],b[5010][5010],totdeg,n,m,succ[5010],prev[5010],faqcnt=0;
      map <int,int> mp;
      
      struct data{
          int x,y,k,num;
      }sta[5000*5000+1];
    
      ULL ran(){
          sed*=1280313;sed+=12413;sed^=123893;
          return(sed);
      }
    
      void restore(int tar){
          while (top>tar){
            data t=sta[top];top--;
            int x=t.x,y=t.y,i=t.k,num=t.num;
          if (t.k==-1){
              deg[x]+=num;deg[y]+=num;
            b[x][y]=b[y][x]=num;
            totdeg+=2*num;
            succ[prev[x]]=x;prev[succ[x]]=x;
          }else{
              deg[x]+=num;deg[y]-=num;
              b[y][i]=(b[i][y]-=num);
              b[x][i]=b[i][x]=num;
          }
        }
      }
    
      void contract(int x,int y){
        sta[++top]=(data){x,y,-1,b[x][y]};
        deg[x]-=b[x][y];deg[y]-=b[x][y];
        totdeg-=2*b[x][y];
        b[x][y]=b[y][x]=0;
        prev[succ[x]]=prev[x];succ[prev[x]]=succ[x];
          for (int i=0;i<=n;i=succ[i]) if (b[x][i]){
            sta[++top]=(data){x,y,i,b[x][i]};
            deg[x]-=b[x][i];deg[y]+=b[x][i];
            b[y][i]=(b[i][y]+=b[x][i]);
            b[x][i]=b[i][x]=0;
        }
      }
    
      void del(){
          int tar=ran()%totdeg+1,x,y;
          for (x=0;tar>deg[x];tar-=deg[x],x=succ[x]);
          for (y=0;tar>b[x][y];tar-=b[x][y],y=succ[y]);
          contract(x,y);
      }
    
      int slowsolve(int po){
        int ttop=top;
          for (int i=1;i<=po-2;i++) del();
        int ret=totdeg/2;
          restore(ttop);
          return(ret);
      }
    
      int fastsolve(int po){
        int ttop=top,ret=1e9;mp[po]++;
          if (po<=20){
            int ret=1e9;
            for (int i=1;i<=100;i++){
                ret=min(ret,slowsolve(po));
                restore(ttop);
          }
          return(ret);    
        }
        
        int tar=ceil(1+po/sqrt(2));
        for (int i=1;i<=po-tar;i++) del();
        ret=min(ret,fastsolve(tar));
        restore(ttop);
        
        for (int i=1;i<=po-tar;i++) del();
        ret=min(ret,fastsolve(tar));
        restore(ttop);
        return(ret);
      }
    
      int main(){
          scanf("%d%d",&n,&m);
          prev[n+1]=n;succ[0]=1;
          for (int i=1;i<=n;i++) prev[i]=i-1,succ[i]=i+1;
          for (int i=1;i<=m;i++){
            int t1,t2;
            scanf("%d%d",&t1,&t2);
          deg[t1]++;deg[t2]++;b[t1][t2]++;b[t2][t1]++;
          totdeg+=2;    
        }
        
        int ans=1e9;
        for (int i=1;i<=10;i++){
          int t=fastsolve(n);
          ans=min(ans,t);    
          printf("%d
    ",t);
        }
          
        printf("%d
    ",ans);
      }
  • 相关阅读:
    LeetCode "Jump Game"
    LeetCode "Pow(x,n)"
    LeetCode "Reverse Linked List II"
    LeetCode "Unique Binary Search Trees II"
    LeetCode "Combination Sum II"
    LeetCode "Divide Two Integers"
    LeetCode "First Missing Positive"
    LeetCode "Clone Graph"
    LeetCode "Decode Ways"
    LeetCode "Combinations"
  • 原文地址:https://www.cnblogs.com/zhujiangning/p/8067609.html
Copyright © 2011-2022 走看看