• POJ 2914


    首先是当年stoer和wagner两位大佬发表的关于这个算法的论文:A Simple Min-Cut Algorithm

    直接上算法部分:

                              分割线                          begin

    在这整篇论文中,我们假设一个普通无向图G=(V,E),其中每条边e都有一个正实数权值w(e)。

      如果我们知道:怎样找到两个节点s,t,以及怎样得到对于s-t的最小割,我们就几乎解决了整个问题:

      定理2.1:

        设s和t是图G中的两个节点,设G/{s,t}是合并s和t后得到的图,

        则图G的全局最小割可以通过“图G对于s-t的最小割”和“图G/{s,t}的全局最小割”得到。

    定理说明:

      若图G存在一个全局最小割,使得s和t被分割,那么,图G的s-t最小割就等于图G的全局最小割。

      否则(即全局最小割没有分割s和t),图G的全局最小割就等于图G/{s,t}的全局最小割。

    因此,我们可以使用一个寻找任意s-t最小割的程序来构建一个递归算法,从而寻找一个图的全局最小割。

    下面的算法(使用一些骚操作搜索方法),可以产生我们想得到的s-t最小割。

    MinimumCutPhase(G,w,a)  //分阶段(phase),每个阶段都产生相应的当前阶段最小割

    {

      A←{a}

      while( A ≠ V )

      {

        选取“最紧密相连的点”加入A

      }

      记录当前阶段的割,并且合并最后加入的两个点

    }

    呵,听起来就很玄乎,什么玩意儿呢,下面有解释。

    A是作为V的一个子集,开始时是空集,我们任取一个点,加入到A中,然后通过某个规则不断地往A中加入点,直到A == V为止,那是什么规则呢?

      在每一步的加点操作中,我们选择集合(V - A)中和A“最紧密相连的点”加入A,那什么是“最紧密相连的点”呢?

        设:点y∈ V - A ,且点y为与集合A直接相连的点,所有点y的集合为Y;

          所有与集合A直接相连的边的权重总和为w(A,y);

        则:“最紧密相连的点” z 满足: z ∈ Y,并且w(A,z)为所有w(A,y)中最大的。

        通俗的说,就是V - A集合里,找一个直接与A相连的点,这个点是 Σ(该点所有与A直接相连的边的权值) 最大的那个。

    (不难看出,这是一种类似于prim算法生成类似最大生成树的算法)

    在我们不断往A加点的过程中,记录下最后加入A的两个点,记为s和t,对这两个点进行合并操作(merge):

      删除点s和点t,加入新的点u作为代替,所有原本从s或t点出发的边(edge< s , t >除外,这条边删除;并且设这些边到达的点为x),都用一条新的edge< u , x >代替;

      并且 edge< u , x >.weight = edge< s , x >.weight + edge< t , x >.weight ;  (若某条边不存在,则定义其weight=0)

      (当然需要注意的是,所有讨论都是建立在无向图上,故这里的出发、到达不代表这条边的方向,只是单纯描述该边的两个端点)

    然后,我们把当前分割s和t的割(cut),叫做阶段割the-cut-of-phase),不难得到,阶段割等于w(A,t) (注意,此处的集合A表示加入点t前的集合A);

    then,所有阶段割中最小的,即本算法的结果,即我们想求的全局最小割。

    MinimumCut(G,w,a)

    {

      while(|V| > 1)

      {

        MinimumCutPhase(G,w,a) 

        if(阶段割 < 当前全局最小割) 当前全局最小割 = 阶段割

      }

    }

    最后,注意到节点a在本算法整个过程中是一直不变的,实际上它也可以在每个阶段(phase)进行任意选择。

                                分割线                          end

    然后是论文中的example部分:

                                分割线                          begin

    这是第一次MinimumCutPhase(G,w,a)操作前和操作后的样子,

    在此次MinimumCutPhase(G,w,a)操作中,进入集合A的点的顺序为: 2 → 3 → 4 → 7 → 8 → 6 → 5(s) → 1(t) ;

    显然,此次的阶段割cut-of-the-phase.weight = w(A,t) = edge<1,2>.w + edge<1,5>.w = 5 ;

    之后的操作与此类似,不再赘述,可以自行对照论文中的FIG。

                                分割线                          end

    当然,上stoer和wagner两位巨老当初的论文,装逼成分多余实际效用= =,感觉自己翻译也翻译的像一坨shit一样;

    更多细节更深理解还请看中文:http://files.cnblogs.com/files/dilthey/stoer-wagner%E7%AE%97%E6%B3%95.pdf

    先上算法模板以及解释:

     1 #include<cstring>
     2 #define MAXN 505
     3 #define INF 0x3f3f3f3f
     4 int n,m;//n个点,m条边
     5 int edge[MAXN][MAXN],dist[MAXN];
     6 bool vis[MAXN],bin[MAXN];
     7 void init()
     8 {
     9     memset(edge,0,sizeof(edge));
    10     memset(bin,0,sizeof(bin));
    11 }
    12 int merge(int &s,int &t)//对应论文中的MinimumCutPhase()
    13 {
    14     memset(dist,0,sizeof(dist));
    15     memset(vis,0,sizeof(vis));
    16     int k,mincut,maxc;
    17     for(int i=1;i<=n;i++)
    18     {
    19         k=-1, maxc=-1;
    20         for(int j=1;j<=n;j++) if(!bin[j] && !vis[j] && dist[j] > maxc) {k=j;maxc=dist[j];}
    21             //寻找"the most tightly connected vertex" 
    22         if(k == -1) return mincut;
    23         vis[k]=true;//点k加入集合A 
    24         s=t; t=k;//不断移动s和t,保证他们是最后进入集合A的两个点 
    25         mincut=maxc;//不断更新mincut为w(A,t)
    26         for(int j=1;j<=n;j++) if(!bin[j] && !vis[j]) dist[j]+=edge[k][j];//计算所有的w(A,y) 
    27     }
    28     return mincut;
    29 }
    30 int stoer_wagner()
    31 {
    32     int mincut=INF,s,t,ans;
    33     for(int i=1;i<=n-1;i++)//merge(s,t)一次减少一个点,|V|要从n减少到1,故要进行n-1次 
    34     {
    35         ans=merge(s,t);
    36         bin[t]=1;//把t合并到s中 
    37         if(ans<mincut) mincut=ans;
    38         if(mincut==0) return 0;
    39         for(int j=1;j<=n;j++) if(!bin[j]) edge[s][j]=(edge[j][s]+=edge[j][t]);//更新所有从s出发的边 
    40     }
    41     return mincut;
    42 }
    View Code

    时间复杂度O(n³);

    再说POJ2914,

    题目链接:http://poj.org/problem?id=2914

    有了模板之后,就是模板题;

    当然,需要注意的是,这道题目里,n个点标号是0~n-1,用这个模板的话需要在输入的时候自己+1;

    另外,这道题目的输入有重边,所以存边的时候,使用“+=”,而不是“=”;

     1 #include<cstdio>
     2 #include<cstring> 
     3 #define MAXN 505
     4 #define MAXM MAXN*MAXN/2
     5 #define INF 0x3f3f3f3f
     6 using namespace std;
     7 int n,m;
     8 int edge[MAXN][MAXN],dist[MAXN];
     9 bool vis[MAXN],bin[MAXN];
    10 void init()
    11 {
    12     memset(edge,0,sizeof(edge));
    13     memset(bin,0,sizeof(bin));
    14 }
    15 int merge(int &s,int &t)
    16 {
    17     memset(dist,0,sizeof(dist));
    18     memset(vis,0,sizeof(vis));
    19     int k,mincut,maxc;
    20     for(int i=1;i<=n;i++)
    21     {
    22         k=-1, maxc=-1;
    23         for(int j=1;j<=n;j++) if(!bin[j] && !vis[j] && dist[j] > maxc) {k=j;maxc=dist[j];}
    24         if(k == -1) return mincut;
    25         vis[k]=true;
    26         s=t; t=k; 
    27         mincut=maxc;
    28         for(int j=1;j<=n;j++) if(!bin[j] && !vis[j]) dist[j]+=edge[k][j];
    29     }
    30     return mincut;
    31 }
    32 int stoer_wagner()
    33 {
    34     int mincut=INF,s,t,ans;
    35     for(int i=1;i<=n-1;i++)
    36     {
    37         ans=merge(s,t);
    38         bin[t]=1;
    39         if(ans<mincut) mincut=ans;
    40         if(mincut==0) return 0;
    41         for(int j=1;j<=n;j++) if(!bin[j]) edge[s][j]=(edge[j][s]+=edge[j][t]);
    42     }
    43     return mincut;
    44 }
    45 int main()
    46 {
    47     while(scanf("%d%d",&n,&m)!=EOF)
    48     {
    49         init();
    50         for(int i=1;i<=m;i++)
    51         {
    52             int a,b,c;
    53             scanf("%d%d%d",&a,&b,&c);
    54             edge[a+1][b+1]+=c;
    55             edge[b+1][a+1]+=c; 
    56         }
    57         printf("%d
    ",stoer_wagner());
    58     }
    59 }
  • 相关阅读:
    oracle grant授权的理解
    SQL SERVER 2005 版本以上 CTE递归查询的实现
    常用的加密解密技术之——【加密原理】
    常用的汇编指令
    常用加密解密技术之——【DES算法实现过程分析】
    VC6.0编译器中混有.c文件时出现fatal error C1853错误解决办法
    C++ Primer读书笔记
    Shot(数学+物理题,不简单)
    [置顶] C++/C 数据类型的取值范围
    <fzu1922>非主流
  • 原文地址:https://www.cnblogs.com/dilthey/p/7399281.html
Copyright © 2020-2023  润新知