网络流 EdmonsKarp 算法讲解

网络流EK算法
数据结构:队列
主要操作:广搜 记录路径 更新
能解决的问题:最大流(最小割)
复杂度:O(MV)v指最大容量,M指边数。
新名词:
    1.增广路:从源点source到tink的一条简单路,如果路上的每条边(u,v)的可改进量均大于0,则称这条路为一条增广路。
    增广路定理:网络达到最大流量当且仅当不存在增广路。
    增广路算法:从一个可行流开始不断的寻找可增广路,然后沿着它增广,直到它不存在。
    2.反向弧:如果有一条弧(u,v),那么再进行网络流算法时,要对它建立反向弧(v,u),反向弧的容量为0,与正向弧相反,正向弧减少容量时,反向弧增加容量。建立反向弧能更多的增广,使网络流算法正确。(无法给出证明)
    3.可行弧:在EK算法中指可从u到v增加流量(也就是容量不为0)。
    4.可行路:从源点source到tink的有可行弧构成的路径叫做可行路。    

具体操作:
    1.建立网络(正向弧+反向弧)
    2.从源点出发,广搜一条最短可行路(即最先到达汇点tink的那条路),每次到达一个点用pre数组记录是由那条边过来的(为后面减小流量做准备)
    3.到达汇点tink时,按照pre数组记录的,从可行路中找一条容量最小的进行容量减少。即找到了一条割边。
    4.重复操作2.3,直到无法到达汇点,算法结束,即找到了最大流,算法结束。

推荐水题:USACO 4.2.1 纯网络流算法,可用来试EK的正确性,但EK只能拿50分!
  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 using namespace std;
  6 
  7 #define INF 1000000  
  8 
  9 struct    Edge
 10 {
 11     int x,y,flow,next,op;//flow表示当前边的容量  op表示正向弧所对应的反向弧在边表中的位置
 12 };
 13 
 14 Edge map[200];
 15 int que[20000];
 16 bool b[500];//用b数组记录是否入队,可以防止重复入队,因为有反向弧和环!!
 17 int source,tink,n,m,tot,ans,head,tail;
 18 int first[500];
 19 int pre[500];
 20 
 21 int fmin(int x ,int y)
 22 {
 23     if (x<y)    return x;
 24     else return y;
 25 }
 26 
 27 void Build(int x ,int y ,int c)
 28 {
 29     tot++;//正向弧
 30     map[tot].x=x;
 31     map[tot].y=y;
 32     map[tot].flow=c;
 33     map[tot].next=first[x];
 34     first[x]=tot;
 35     map[tot].op=tot+1;
 36     tot++;//反向弧
 37     map[tot].x=y;
 38     map[tot].y=x;
 39     map[tot].flow=0;//反向弧容量为0
 40     map[tot].next=first[x];
 41     first[x]=tot;
 42     map[tot].op=tot-1;
 43 }
 44 
 45 void init()
 46 {
 47     memset(first,0,sizeof(first));
 48     tot=0;     ans=0;
 49     scanf("%d%d",&m,&n);
 50     for (int i=1; i<=m; i++)
 51     {
 52         int x,y,c;
 53         scanf("%d%d%d",&x,&y,&c);
 54         Build(x,y,c);                
 55     }
 56     source=n+1;     tink=n+2;//手动建立源点和汇点,看题目要求!
 57     Build(source,1,INF);
 58     Build(n,tink,INF);
 59 }
 60 
 61 bool Bfs()
 62 {
 63     memset(b,false,sizeof(b));
 64     head=1;    tail=1;     que[1]=source;    b[source]=true;    pre[source]=-1;//没有可以到源点的路
 65     while (head<=tail)
 66     {
 67         int x=que[head];
 68         int t=first[x];        
 69         while (t>0)
 70         {
 71             int y=map[t].y;            
 72             if (map[t].flow>0    &&    b[y]==false)//找到一条可行弧
 73             {                
 74                 tail++;
 75                 que[tail]=y;
 76                 b[y]=true;
 77                 pre[y]=t;//路径记录
 78                 if (y==tink)    return true;//到达汇点,退出广搜
 79             }
 80             t=map[t].next;
 81         }
 82         head++;
 83         b[x]=false;
 84     }    
 85     return false;
 86 }
 87 
 88 void updata()
 89 {    
 90     int Min=INF;
 91     for (int p=pre[tink];p!=-1;p=pre[map[p].x])    
 92     {
 93         Min=fmin(Min , map[p].flow);//找可行路中的最小容量
 94     }
 95     for (int p=pre[tink];p!=-1;p=pre[map[p].x])//更新容量过程    
 96     {
 97         map[p].flow-=Min;
 98         map[map[p].op].flow+=Min;//反向弧增加容量
 99     }
100     ans+=Min;    
101 }
102 
103 void EK()
104 {
105     
106     while (Bfs()==true)
107     {
108         updata();    
109     }
110     printf("%d\n",ans);    
111 }
112 
113 int main()
114 {
115     freopen("ditch.in","r",stdin);
116     freopen("ditch.out","w",stdout);
117     init();
118     EK();    
119     return 0;    
120     fclose(stdin); fclose(stdout);
121 }
原文地址:https://www.cnblogs.com/acSzz/p/2477742.html