网络流之最大流

Posted gmsd

tags:

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

暑假就已经学过了,可是因为我太菜了,所以完全没懂,今天老师要我们落实之前学的,就复习了网络流

虽然CSP不考,但是学了也可以用(就像去年D2T3,动态DP模板)

好了,接下来进入正题:

首先,啥子事网络流,官方解释如下

在图论中,网络流(英语:Network flow)是指在一个每条边都有容量(capacity)的有向图分配流,使一条边的流量不会超过它的容量。
通常在运筹学中,有向图称为网络。顶点称为节点(node)而边称为弧(arc)。一道流必须匹配一个结点的进出的流量相同的限制,除非这是一个源点(source)──有较多向外的流,
或是一个汇点(sink)──有较多向内的流。一个网络可以用来模拟道路系统的交通量、管中的液体、电路中的电流或类似一些东西在一个结点的网络中游动的任何事物。

嘿嘿,好像不懂,简单一点来说:

有一个储水站,给一个小区分配水,而你又住在里面

每家每户都有不同规格的水管,也就是说,在速度不变的情况下,同一时间内,流动的水的多少不同

而且从储水站里面送出来的速度必须一样,因为流速越快,液体压强越大

我们当然不希望水管炸裂

每一户人家用完的水肯定要运输到一个废水处理站

我们需要求的是储水站最多可以送出多少水,最后能够送到废水处理站(因为要回收利用)

 

我第一次看到这个题目时,产生了一段对话:

我:这个不就是暴力枚举吗???
某巨佬:......这个超时了
我:......那怎么做
某巨佬:EK或者是Dinic
我:???什么玩意儿

首先明确几个概念:

容量:每条边都有一个容量(水管的最大水流容量)

源点:出发点(储水站)。

汇点:结束点(废水处理站)。

流:一个合法解称作一个流,也就是一条可以从源点到汇点的一条合法路径。

流量:每条边各自被经过的次数称作其流量,最终收集的总数为整个流的流量。

增广路:在当前网络之后找到一条能够从源点到汇点能运更多货物的路径。当一条边被增广之后(即它是增广路的一部分,或者说增广路通过这条边),这条边还能通过的流量,叫做剩余流量,修改之后的图称为残量网络。

 

首先是我最喜欢的EK算法(主要是代码易懂,简洁,适合我这种蒟蒻)

它的思想很简单,每一次疯狂的寻找增广路,当然如果其中有一条边为‘0’,就不是增广路,将当前的流量减去中间的最小值(肯定的,因为如果不是最小值水就不能通过)

ans加上当前这个最小值,直到寻找不到增广路

但是有瑕疵,可以把这个hack掉:

技术图片

我们可以轻易知道,答案为2,但是如果按照以上思想:

程序会这样跑,以至于ans=1:

技术图片

所以,我们需要给这个程序一个后悔的机会

是不是想到dfs回溯,很明显,超时了

所以我们想到了一个巧妙的办法,建反边,初始值为零

在找增广路的时候,设这条路的最小流量为opt

那么,反边的值+opt,正边-opt

我们可以这么想,我们走反的边相当于将水退了回去

好的,就这样,AC代码如下:

#include<bits/stdc++.h>
using namespace std;

const int N=300002;
int spot,EDGE,S,E;
int head[N],ver[N],nxt[N],tot,edge[N];
int q[N],hd,tl,res[N],now[N],pre[N],ans;

int read()
    int s=0,w=1;char ch=getchar();
    while(ch<0||ch>9)w=(ch==-)?-1:1,ch=getchar();
    while(ch>=0&&ch<=9)s=s*10+ch-0,ch=getchar();
    return s*w;


void write(int x)
    if(x<0) putchar(-);x=-x;
    int y=10,len=1;
    while(y<=x) y*=10;len++;
    while(len--)y/=10;putchar(x/y+48);x%=y;


void add(int x,int y,int z)
    ver[++tot]=y;edge[tot]=z;nxt[tot]=head[x];head[x]=tot;


bool Bfs()
    memset(res,0,sizeof(res));
    hd=0;tl=1;q[1]=S;res[S]=1;now[S]=1e9;
    while(hd^tl)
        hd++;
        int x=q[hd];
        for(int i=head[x];i;i=nxt[i])
            int y=ver[i];
            if(!res[y]&&edge[i]>0)
                now[y]=min(now[x],edge[i]);pre[y]=i;
                if(y==E)return 1;
                tl++;q[tl]=y;res[y]=1;
            
        
    
    return 0;


void EK()
    ans+=now[E];
    int x=E;
    while(x!=S)
        edge[pre[x]]-=now[E];edge[pre[x]+1]+=now[E];
        x=ver[pre[x]+1];
    


int main()
    EDGE=read();S=1;E=read();
    for(int i=1;i<=EDGE;i++)
        int x=read(),y=read(),z=read();
        add(x,y,z);add(y,x,0);
    
    while(Bfs())EK();
    write(ans);
    return 0;

//   10-2   //

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

C#流总结(文件流内存流网络流BufferedStreamStreamReader/StreamWriterTextReader/TextWriter)

C#流总结(文件流内存流网络流BufferedStreamStreamReader/StreamWriterTextReader/TextWriter)

网络流问题

网络流

网络流笔记

待更新算法