网络流初步

Posted bhllx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流初步相关的知识,希望对你有一定的参考价值。

网络流初步

P.S.

简单的学习了一下,以后方便复习。

相关概念

  • 源点:只有出边没有入边的点。
  • 汇点:只有入边没有出边的点。
  • 容量和流量:每条有向边上有两个量,容量和流量。从i到j的容量通常用c(i,j)表示,流量则通常是f(i,j)。

相关性质

  • 容量限制:f(u,v)≤c(u,v)
  • 反对称性:f(u,v) = - f(v,u)
  • 流量守恒:对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。

最大流

对于一个网络,合法的流中,使每一条边的流量之和最大的流。

EK增广路算法:

该算法的核心在于:

用bfs寻找一条从源点到汇点的增广路,并记录下这条路上最小的剩余容量,作为本条路上本次通过的流量。

然后直到找不到这样的增广路时,就结束算法,此时得到最大流。

但是注意到每次寻找是随机的,这样会对后续造成影响,从而可能得不到真正的最大流。

所以需要给每个流反悔的机会,即“退流”,用于给更好的方案让路。

这可以用建立反向边来实现,当正向边经过一个流时,反向边对应的增加这个流的流量即可。

dinic算法:

该算法是基于对EK的优化,可以发现EK的每次bfs都只寻找到了一条增广路,而同时却可能遍历整个残量网络。

而dinic则是每次遍历完残量网络后,找到了多条增广路,并完成了流量的更新。

核心内容:

首先bfs在原图上建立分层图。然后沿着分层图多路增广即可。

分层图的构造:

IL int bfs() 
    while(!q.empty()) q.pop();
    memset(d,0,sizeof(d));
    d[S]=1,q.push(S);
    while(!q.empty()) 
        RG int i,y,x=q.front(); q.pop();
        for(i=head[x];i;i=e[i].next)
            if(e[i].v&&!d[y=e[i].to]) 
                d[y]=d[x]+1,q.push(y);
                if(y==T) return 1;
            
    
    return 0;

当前弧优化:

在固定的一张分层图中,到达一个点的路径是固定的。所以在这种情况下到达了一次某个点后,之前走过的这个点的出边

就没有必要再走了,所以直接记录一下这个点下一条该走的出边即可。由于它是对于一张固定的分层图才能这样,所以每

次bfs重建分层图后需要重置该数组。


int dfs(int x,int flow) 
    if(x==T) return flow;
    RG int i,y,k,rest=flow;
    for(i=cur[x];i&&rest;i=e[i].next)
        if(d[y=e[i].to]==d[x]+1&&e[i].v) 
            cur[x]=i; // Here 
            k=dfs(y,min(rest,e[i].v));
            if(!k) d[y]=0;
            rest-=k,e[i].v-=k,e[i^1].v+=k;
        
    return flow-rest;


IL void dinic() 
    RG int i,j,flow,maxflow=0;
    while(bfs()) 
        memcpy(cur,head,sizeof(head)); //reset
        while(flow=dinic(S,inf)) maxflow+=flow;
    

最小割

最大流最小割定理:一个网络最大流量=最小割中边的容量之和。

费用流

注意此处涉及的费用流是先最大流再最小(大)费用的情况。

一般有spfa-EK或spfa-dinic来求解。即把二者的spfa代替掉bfs即可。

比如dinic费用流的分层图构建部分:


IL int spfa() 
    memset(d,0x3f,sizeof(d));
    while(!q.empty()) q.pop();
    d[S]=0,q.push(S);
    while(!q.empty()) 
        RG int i,y,x=q.front();
        in[x]=0,q.pop(); //spfa 可以多次入队!
        for(i=head[x];i;i=e[i].next)
            if(e[i].v&&d[y=e[i].to]>d[x]+e[i].w) 
                d[y]=d[x]+e[i].w;
                if(!in[y]) in[y]=1,q.push(y);
            
    
    return d[T]!=inf;

the end

这大概就是基础网络流知识了,关于上下界网络流的学习就看这里吧。

以上是关于网络流初步的主要内容,如果未能解决你的问题,请参考以下文章

网络流初步

网络流初步

网络流初步

初步网络最大流的算法及详解

网络流初步

网络流初步