文章目录

  • 前言
  • 关于最大流
  • 神奇的术语
  • EK算法
  • Dinic
  • 时间复杂度
    • EK
    • Dinic
  • 细节与一些神奇的性质
    • 反向弧的作用以及代码边中的c
    • 合法的f对应流
    • st有入边,ed有出边
    • 双向边的两种处理方法
    • s<f优化
    • 反向边本次无用性
    • Dinic深度严格单调递增性
    • 从起点跑和从终点跑
    • 反向边处理方法
    • 当前弧优化
  • 参考资料

前言

初三的时候就知道以后注定会重新写网络流的博客了。

但是呢,之前的博客是不会删的。水数量

因为之前碰了很多杂七杂八的东西。

万一删了不就前功尽弃了,如果有少数几个读得懂我所写的文章的,可以结合两篇一起看,遇到重复的地方以这篇为参考,加上自己的理解。

需要注意的是,这篇文章可能对于信息学新手不会太友好,如果你只是个新手,建议去看看我之前的那篇,那篇提供了一个例子的讲解,会比较好,而这篇文章注重的是理论,比较干。说实话,就是懒得写例子

当然,这篇文章写的比较仓促,因为还要备战NOIP。

关于最大流

最大流是啥?

想象一坨有向的水管,每个水管连接着两个点,且图中有个入水口,有个出水口,出水口可以无限出水,入水口可以瞬间收水。

但是呢,在一个单位时间,一个水管只能通过cic_{i}ci​单位体积的水,同时水瞬间通过这个水管,并在同一个单位时间通过其余的水管,且水管不能存水,如果水不能通过这个水管一瞬间流到终点,那么水不会流过来。有生命的水

那么,一个单位时间内入水口最多入多少体积的水?这就是最大流问题。


其中a/ba/ba/b分别表示流过的水和最多流过多少的水,AAA为出水口,EEE为入水口。

可以看到,上面两个就是同一个图的最大流,有两种,其中,为什么第一个图中,ACACAC流过的流量是000呢?因为如果从AAA点流出了一体积的水到了CCC又到了DDD,无法到达终点EEE,所以这一体积的水不会流过CCC,而是选择留在原地。

当然,上述的表示非常的SB,因为我确实不知道如何比较规范的用中文表述。

我们用(i,j)(i,j)(i,j)表示一条边,c(i,j)c(i,j)c(i,j)表示这条边最多流过的水的体积,f(i,j)f(i,j)f(i,j)表示这条边流过的水的体积。

那么有以下规定。

  1. (i,j)∈E,0≤f(i,j)≤c(i,j),(i,j)∉E,c(i,j)=0(i,j)∈E,0≤f(i,j)≤c(i,j),(i,j)∉E,c(i,j)=0(i,j)∈E,0≤f(i,j)≤c(i,j),(i,j)∈/​E,c(i,j)=0
  2. ∑(x,i)∈Ef(x,i)=∑(j,x)∈Ef(j,x)(x≠st,ed)\sum\limits_{(x,i)∈E}f(x,i)=\sum\limits_{(j,x)∈E}f(j,x)(x≠st,ed)(x,i)∈E∑​f(x,i)=(j,x)∈E∑​f(j,x)(x​=st,ed)
  3. 在EEE中,ststst不存在入边,ededed不存在出边。(存在就删了)
  4. ∑(st,i)∈Ef(st,i)=∑(j,ed)∈Ef(j,ed)\sum\limits_{(st,i)∈E}f(st,i)=\sum\limits_{(j,ed)∈E}f(j,ed)(st,i)∈E∑​f(st,i)=(j,ed)∈E∑​f(j,ed)

然后要求最大化∑(st,i)∈Ef(st,i)\sum\limits_{(st,i)∈E}f(st,i)(st,i)∈E∑​f(st,i)。

这个时候就有人很敏锐的意识到,那是不是图外面有个自环,这个自环也满足要求?是的,没错,但是我们要求最大化∑(st,i)∈Ef(st,i)\sum\limits_{(st,i)∈E}f(st,i)(st,i)∈E∑​f(st,i),这个环我们管不管无所谓。

神奇的术语

请注意:由于我的学习经常都是不学术的,所以这些术语的表达甚至意思可能与真实的术语有一定的偏差,见谅。但是应该不影响看这篇文章

弧:说白了就是有向边。
反向弧:如果(x,y)∈E(x,y)∈E(x,y)∈E,那么(y,x)(y,x)(y,x)就是(x,y)(x,y)(x,y)的反向弧。

我看算法导论的时候发现正规的网络流定义EEE中的反向弧必须不属于EEE,但是实际上如果属于EEE也不会影响算法,但是呢,为了后面的表述方便,我们也是默认(y,x)(y,x)(y,x)不属于EEE,但是如果题目要求呢?实际上我们有类似的转换:

可以在不影响结果的情况下保证反向弧不在EEE中。

反向弧的f,cf,cf,c:(i,j)∈E,f(j,i)=−f(i,j),c(j,i)=0(i,j)∈E,f(j,i)=-f(i,j),c(j,i)=0(i,j)∈E,f(j,i)=−f(i,j),c(j,i)=0,(有没有人规定非EEE元素的fff一定非负)。

网络:就是点边形成的有向图,其中有意义的边只有EEE和其反向弧。

流量网络:网络每条边标出其fff。

容量网络:网络每条边标出其ccc。

残余网络:流量网络−-−残余网络。(这里"−-−"的意思就是边上标号相减)

增广路径:从ststst到ededed的一条路径,且路径上的每条边f<cf<cf<c,而这条路径ppp的流量f(p)f(p)f(p)就是路径中f−cf-cf−c的最小值。

举个例子:

当然,这里默认网络中没有意义的边(比如残余网络中标号为000的边,容量网络中标号为000的边)直接消失即可,画图方便一点。(updata:当然,在后面证明的过程中,我们会发现,这些容量流量都为000的边没有意义,不予讨论,而且,在某些证明中,是只针对f>0f>0f>0的EEE的边,在你发现证明看不懂的时候,或者存在很大的逻辑问题的时候,可以考虑看看是不是考虑了没有意义的边)

EK算法

这个算法是根据一个依据:网络中不存在增广路时就是最大流来搞的。

精髓就是每次只增广最短的路径(默认边的长度都是111),因此只要不断的跑分层图,然后不断增广即可。

需要注意的是,一条边的fff改变时,其反向弧也要改变。

但是我没有代码QMQ,因为直接用Dinic的。

Dinic

我们发现,一次建图就跑一条增广路,真的是浪费!!!!

因此我们多跑几次增广路。

例题:https://www.luogu.com.cn/problem/P3376

代码如下:

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
struct  node
{LL  x,y,c/*还能流多少的流量*/,next,other/*反向弧的编号*/;
}a[250000];LL  len,last[1300],st,ed,n,m;
void  ins(LL  x,LL  y,LL  c)
{LL  k1,k2;len++;k1=len;a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;len++;k2=len;a[len].x=y;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;a[k1].other=k2;a[k2].other=k1;
}
LL  list[1300],head,tail,h[1300];
bool  bt_h()
{memset(h,0,sizeof(h));h[st]=1;list[1]=st;head=1;tail=2;while(head!=tail){LL  x=list[head];for(LL  k=last[x];k>0;k=a[k].next){LL  y=a[k].y;if(a[k].c>0  &&  h[y]==0){h[y]=h[x]+1;list[tail++]=y;}}head++;}if(h[ed])return  true;else  return  false;
}
LL  mymin(LL  x,LL  y){return  x<y?x:y;}
LL  findflow(LL  x,LL  f)
{if(x==ed)return  f;LL  s=0,t;for(LL  k=last[x];k>0;k=a[k].next){LL  y=a[k].y;if(a[k].c>0  &&  h[y]==h[x]+1  &&  s<f/*没有跑满*/){s+=(t=findflow(y,mymin(a[k].c,f-s)));a[k].c-=t;a[a[k].other].c+=t;}}if(s<f)h[x]=0;//如果这个点跑不满,以后都不到这个点了return  s;
}
int  main()
{scanf("%lld%lld%lld%lld",&n,&m,&st,&ed);for(LL  i=1;i<=m;i++){LL  x,y,c;scanf("%lld%lld%lld",&x,&y,&c);ins(x,y,c);}LL  s=0;while(bt_h()==true){s+=findflow(st,LL(999999999999));}printf("%lld\n",s);return  0;
}

时间复杂度

EK

事实上,对于h[x]h[x]h[x],不断的增广,h[x]h[x]h[x]只会非严格单调递增,设增广后为h′h'h′,增广前在残余网络中有意义的边构成的集合为EEE。

什么?如何证明?

我们设一次增广后h[x]h[x]h[x]减少了,且xxx是所有减少的点中h′h'h′最小的点。

设ststst到xxx的路径为st⇝y→xst⇝y→xst⇝y→x。

分类讨论y−>xy->xy−>x。

如果(y,x)∈E(y,x)∈E(y,x)∈E,h[x]≤h[y]+1≤h[y]′+1≤h[y]h[x]≤h[y]+1≤h[y]'+1≤h[y]h[x]≤h[y]+1≤h[y]′+1≤h[y]。
如果(y,x)∉E(y,x)∉E(y,x)∈/​E,说明(x,y)(x,y)(x,y)是在增广路径中的,所以h[x]=h[y]−1≤h[y]′−1,h[x]′=h[y]′+1≥h[x]+2h[x]=h[y]-1≤h[y]'-1,h[x]'=h[y]'+1≥h[x]+2h[x]=h[y]−1≤h[y]′−1,h[x]′=h[y]′+1≥h[x]+2。

所以hhh单调递增。

现在证明,一条增广路径ppp,如果一条边在路径上且c−fc-fc−f等于f(p)f(p)f(p),那么这条边被称为关键边,本次增广完之后便会在残余网络中没有意义。

那么其重新有意义需要图中带来怎样的变化呢?其重新有意义, 必须其反向弧存在于增广路中。

即对于弧(x,y)(x,y)(x,y),反向弧存在于增广路中:h[x]=h[y]−1,h[x]′=h[y]′+1≥h[y]+1≥h[x]+2h[x]=h[y]-1,h[x]'=h[y]'+1≥h[y]+1≥h[x]+2h[x]=h[y]−1,h[x]′=h[y]′+1≥h[y]+1≥h[x]+2。

即要求xxx的深度加222,那么对于一条边,其最多变成关键边n−12\frac{n-1}{2}2n−1​次,所以时间复杂度就是:O(nm2)O(nm^2)O(nm2)的。

但是需要注意的是,xxx的深度+2+2+2并不代表只让一条边有意义,比较容易陷入的误区是:深度之和最多是n2n^2n2的,那么一个点深度+2+2+2不是只会让一条边有意义吗?那不就是最多n22\frac{n^2}{2}2n2​次增广。但是一个点深度+2+2+2不一定只让一条边有意义啊!!!!例子以后补。

当然,这也有个推论,只要我每次找到的增广路都保证是图中长度最小的,那么增广路长度一定非严格递增。

Dinic

DinicDinicDinic时间复杂度为什么是正确的?依据EKEKEK算法的推论,我们可以在一次建分层图的时候直接把本次分层图中所有增广路一下子找出来,这样不就免了多次减分层图的时间了吗?

因此,时间复杂度还是O(nm2)O(nm^2)O(nm2)的。(但事实上这是不是理论上界呢?不是说n2mn^2mn2m吗?这个会在弧优化的时候具体分析)

细节与一些神奇的性质

反向弧的作用以及代码边中的c

下文默认是找增广路,不管是用什么算法,反正就是找增广路。

有人可能会问,为什么反向弧这条边不存在于原图当中,为什么能够在增广路径的时候走过它?

先思考反向弧在残余网络中有意义的原因?

是因为原弧曾经存在于增广路径中。

其实反向弧的存在,就是提供了一次后悔操作。

在下图中:

(红边表示正在找增广的边)

我们发现,BBB堵住了,其原因是因为(A,C)(A,C)(A,C)没有走(C,D)(C,D)(C,D),别跟我说什么长度最小,随便改一下照样卡,那么我们就设置反向边,叫做后悔,至于其意义,后面具体讲,至少我们发现设置反向边后,(A,B)(A,B)(A,B)就可以走(B,C)(B,C)(B,C)直接到DDD了。

其意义现在讲,对于路径p1p1p1:st⇝x→y⇝edst⇝x→y⇝edst⇝x→y⇝ed,对于路径p2p2p2:st⇝y→x⇝edst⇝y→x⇝edst⇝y→x⇝ed,那么其实质上就是走了两条路径:st⇝x⇝ed,st⇝y⇝edst⇝x⇝ed,st⇝y⇝edst⇝x⇝ed,st⇝y⇝ed。

即:

这就是反向边的真正含义,在对应到fff上面,对于(i,j)∈E(i,j)∈E(i,j)∈E,如果本次流过(j,i)(j,i)(j,i)流了kkk(很明显k≤f(i,j)k≤f(i,j)k≤f(i,j),因为f(i,j)≥0f(i,j)≥0f(i,j)≥0),那么其意义上就是(i,j)(i,j)(i,j)之前有kkk的流量取消了(上图中x−>yx->yx−>y),所以在f(j,i)+=k,f(i,j)−=kf(j,i)+=k,f(i,j)-=kf(j,i)+=k,f(i,j)−=k。

现在聊聊代码实现中的边的ccc代表什么。

对应在代码中的实现,边的ccc表示残余流量(下文用c′c'c′表示,其实就是残余网络中边的标号),即c(i,j)−f(i,j)c(i,j)-f(i,j)c(i,j)−f(i,j),就是c′(i,j)c'(i,j)c′(i,j)(不难发现在代码中c′c'c′严格≥0≥0≥0,满足上面的对于c,fc,fc,f的约束),对应一下就是c′(j,i)−=k,c′(i,j)+=kc'(j,i)-=k,c'(i,j)+=kc′(j,i)−=k,c′(i,j)+=k。

而对于(i,j)(i,j)(i,j)流过的流量也是同样如此,同样是c′(i,j)−=k,c′(j,i)+=kc'(i,j)-=k,c'(j,i)+=kc′(i,j)−=k,c′(j,i)+=k。

因此,对于代码中的c′c'c′的处理,是完美的符合其应该代表的含义的。

合法的f对应流

如果fff合法,是不是绝对对应着一种流呢?

发现图中只有ststst的入边f=0f=0f=0,出边f≥0f≥0f≥0,出边反之,定义一种网络中只包含f>0f>0f>0且属于EEE的边,这个网络中边的容量为fff,每次拿111流量去从ststst跑到ededed,最终一定会找到∑(st,i)∈Ef(st,i)\sum\limits_{(st,i)∈E}f(st,i)(st,i)∈E∑​f(st,i)条路径(到达一个点的流量和这个点流出的流量相等)。

当然,图中可能还会剩一些环。

需要注意的是,即使你用的是Dinic,也一样可能存在环,举一个例子:

st有入边,ed有出边

不难发现,ededed的出边100100100%不会被经过,ststst的入边也不可能被经过(除非增广路走环),因此不用去提前处理使得ststst没有入边,ededed没有出边。(除非你用的是其他算法)

双向边的两种处理方法

上文也讲了,其实如果原图中就存在双向边有两种处理方法:

  1. 新建一个点,把一条边变成链,即上文做法。
  2. 如果你足够理解反向弧的话,你就会明白,其实反向边可以直接放在一起,如果对于c(i,j)=3,c(j,i)=5c(i,j)=3,c(j,i)=5c(i,j)=3,c(j,i)=5,那么你就按其说的在图中如此设定,这样是完全没有问题的。
    针对c(i,j)=3,c(j,i)=5c(i,j)=3,c(j,i)=5c(i,j)=3,c(j,i)=5,我们说明一下,帮助理解。
    首先,对于这两边只会走一条,两条都走可以交换执行反向边操作,因此,f(i,j)≤0f(i,j)≤0f(i,j)≤0或者f(j,i)≤0f(j,i)≤0f(j,i)≤0是成立的,这样子的话,如果f(i,j)>0f(i,j)>0f(i,j)>0表示原图中只走(i,j)(i,j)(i,j),反之亦然。
  3. 非常SB的方法,当两条边处理,各自建立反向弧,证明方法同2,100%不推荐,除非你有很大的怨念。

s<f优化

为什么代码中s<fs<fs<f就h[x]=0h[x]=0h[x]=0呢?(相当于认为这个点不能走)

难道其不能再做贡献了吗?

事实上是非常肯定的,为了更加直观的理解,我们定义合法网络:
如果对于一条边(x,y)(x,y)(x,y),h[x]=h[y]−1h[x]=h[y]-1h[x]=h[y]−1,那么这条边就在合法网络中。
单纯为了直观理解,其实也没必要

可以发现,边和反向边一定不会同时在合法网络,所以,只有在合法网络中的弧流量才会增加,且绝对不会减少。

因此,xxx到ededed的路径的集合为PPP,这些路径上的边流量只会增加,不会减少,所以这些路径不可能在本次分层图DFSDFSDFS中再度成为增广路,所以xxx以后都不用再找了。

当然,如果你不加这个优化,可以被分分钟卡掉,如下图:

反向边本次无用性

其实从上面大家都看出来了,由于合法网络中弧和反向弧不可能同时出现,所以在这次DFSDFSDFS中,反向弧的流量在减少,但是并不会对DFSDFSDFS产生贡献,所以本次DFSDFSDFS中,你可以先不给反向弧添加流量,放外面添加。

好像并没个卵用

Dinic深度严格单调递增性

定理:Dinic中h[ed]h[ed]h[ed]严格单调递增。

反证法:
本次我们建完了分层图,跑完了流量,这个时候,下一次分层图的h[ed]h[ed]h[ed]是一样的!!!这意味着原本存在一条长度为h[ed]h[ed]h[ed]的增广路径但是我们没跑到!!!!

什么辣鸡东西???

  1. 假设最终增广了kkk条增广路,因此长度之和为:k∗h[ed]k*h[ed]k∗h[ed]。
    其走了本次增广的反向边,因此不能在原有的分层图上增广,假设p1p1p1走了p2p2p2的反向边,这个时候,你就会惊奇的发现,用反向边的真实含义,把p1,p2p1,p2p1,p2调换一下,得到了p1′,p2′p1',p2'p1′,p2′,p1′,p2′p1',p2'p1′,p2′的长度之和为p1,p2p1,p2p1,p2的长度之和−2-2−2,然后不断执行反向边的真实含义,最终导致kkk条增广路径的长度和小于k∗h[ed]k*h[ed]k∗h[ed],那么一定存在一条路径长度小于h[ed]h[ed]h[ed],违反了EKEKEK的那个啥推论。
  2. 都没有走反向边,对于路径ppp,其一定在合法网络中。
    反证法:
    设d[x]d[x]d[x]为路径ppp上xxx到ststst的距离,且xxx在路径ppp上,如果h[x]<d[x]h[x]<d[x]h[x]<d[x],则一定存在一条增广路径小于h[ed]h[ed]h[ed],如果h[x]>d[x]h[x]>d[x]h[x]>d[x],怎么可能?所以一定在合法网络上,所以应该本次就增广了。

从起点跑和从终点跑

好了,开始讲一个完全没有多大用处的优化:从终点开始建分层图会快一点。

从根本上讲,这种优化是针对于DFS找不到增广路径的搜索而言的,实际效果表现不佳很大一部分因为大数据下表现不佳以及BFS本身对于不同的搜索顺序也会有一定的效率影响,在这里提出只是单纯的因为这个优化对于DFS确实是正优化。(同时帮助理解网上说的从ededed建图更加快的理论)

从ststst跑有个非常SB的事情,就是但凡从一个点延伸出来一条链,都很容易跑到这条链里面去。


但是就有人发现了,从终点开始跑可以避免此情况,这不是吹的,这是有科学的依据的。

首先,增广路一定是ededed到ststst的一条路径,从ststst到ededed的深度单调递减且固定减一。

因此,要么这条边一定在ststst到ededed的一条最短路径上,就会被DFSDFSDFS(从起点开始跑同样会走这条边),否则其的深度绝对不会是单调递增的,因此ststst不会去访问他(但是从起点开始跑是可能会去访问的),所以你会发现,从终点开始跑可以在DFSDFSDFS中减掉一些没有必要的状态。

反向边处理方法

对于反向边,代码中采用的是.other.other.other,但是有个更加简单粗暴的方法,一开始设定len=1len=1len=1,这样建边就是2,32,32,3,4,54,54,5这样的编号,而这些编号亦或111就可以互相转换了。

当前弧优化

可以发现,一次DFSDFSDFS,在一个点xxx在跑(x,y)(x,y)(x,y)的边的时候,会出现两种情况,a[k].c<(f−s)a[k].c<(f-s)a[k].c<(f−s),这个时候,xxx会给yyy等于a[k].ca[k].ca[k].c的流量,如果到达终点的流量不足a[k].ca[k].ca[k].c,那么yyy无法访问,这条边作废,如果到达了a[k].ca[k].ca[k].c的流量,这条边满流,作废。

(f−s)≤a[k].c(f-s)≤a[k].c(f−s)≤a[k].c时,会给这条边f−sf-sf−s的流量,如果跑满了,说明这条边尚有余温存在,下次还可以给,如果没有,则yyy无法到达,照样作废。

观察上文,其实就是如果跑完这条边之后,s<fs<fs<f,这条边就作废,所以可以设置curcurcur数组,直接跳过废掉的边,并进行搜索(至于初始化可以在BFSBFSBFS的时候初始化,或者直接用memcpy把lastlastlast赋给curcurcur)。

当然,你可能会问,f−s=a[k].cf-s=a[k].cf−s=a[k].c时,跑满了不照样爆废?反正下次访问也可以O(1)O(1)O(1)重置。

LL  find(int  x,LL  f)
{if(x==ed)return  f;LL  s=0,t;for(int  k=cur[x];k;k=a[k].next){int  y=a[k].y;if(h[y]==h[x]-1  &&  a[k].c>0){s+=(t=find(y,mymin(a[k].c,f-s)));a[k].c-=t;a[k^1/*.other*/].c+=t;if(s==f)return  f;//满足就退出,这步也很重要 }cur[x]=k;//这条边没有全部跑满,直接溜走 }if(s<f)h[x]=0;return  s;
}

完整代码:

#include<cstdio>
#include<cstring>
#define  N  310
#define  M  11000
using  namespace  std;
typedef  long  long  LL;
struct  node
{int  y,next;LL  c;
}a[M];int  last[N],n,m,len=1/*用异或代替.other*/,st,ed;
int  cur[N];//当前弧
inline  void  ins(int  x,int  y,LL  c)
{len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;len++;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
}
int  h[N],list[N],head=1,tail=n;
inline  bool  bt_()
{memset(h,0,sizeof(h));h[ed]=1;head=1;tail=2;list[1]=ed;while(head!=tail){int  x=list[head++];cur[x]=last[x];/*只对能走到的点记录当前弧*/for(int  k=last[x];k;k=a[k].next){int  y=a[k].y;if(a[k^1].c>0  &&  h[y]==0){list[tail++]=y;h[y]=h[x]+1;}}}return  h[st];
}
template<class  T>
inline  T  mymin(T  x,T  y){return  x<y?x:y;}
LL  find(int  x,LL  f)
{if(x==ed)return  f;LL  s=0,t;for(int  k=cur[x];k;k=a[k].next){int  y=a[k].y;if(h[y]==h[x]-1  &&  a[k].c>0){s+=(t=find(y,mymin(a[k].c,f-s)));a[k].c-=t;a[k^1/*.other*/].c+=t;if(s==f)return  f;//满足就退出,这步也很重要 }cur[x]=k;//这条边没有全部跑满,直接溜走 }if(s<f)h[x]=0;return  s;
}
int  main()
{LL  ans=0;scanf("%d%d%d%d",&n,&m,&st,&ed);for(int  i=1;i<=m;i++){int  x,y;LL  c;scanf("%d%d%lld",&x,&y,&c);ins(x,y,c);}while(bt_()==true)ans+=find(st,LL(9999999999999));printf("%lld\n",ans);return  0;
}

当然,也有人的当前弧是这样写的:

template<class  T>
inline  T  mymin(T  x,T  y){return  x<y?x:y;}
LL  find(int  x,LL  f)
{if(x==ed)return  f;LL  s=0,t;for(int  &k=cur[x];k;k=a[k].next){int  y=a[k].y;if(h[y]==h[x]-1  &&  a[k].c>0){s+=(t=find(y,mymin(a[k].c,f-s)));a[k].c-=t;a[k^1/*.other*/].c+=t;if(s==f)return  f;//满足就退出,这步也很重要 }}if(s<f)h[x]=0;return  s;
}

我不是很喜欢这样写,因为这可能会破坏DinicDinicDinic中h[ed]h[ed]h[ed]单调递增的性质,导致分层图做的比较多(事实上确实会)。

好了,重新分析一波DinicDinicDinic的时间复杂度吧。

EK,DinicEK,DinicEK,Dinic慢在了找增广的时间。

原本没有当前弧优化的时候,每个点xxx如果一个流量都没有,那么其不可以到达,所以讨论有流量的情况,有流量最坏情况下可能需要把xxx的边全部遍历一遍,这意味一条路径可能还需要O(m)O(m)O(m)去找,只不过常数较小(这也是为什么DinicDinicDinic实际表现非常优秀的原因),但是呢,加了当前弧优化(我的写法),点xxx每条边要么有流量,要么被废除,算上废除边的时间复杂度:O(m)O(m)O(m),没经过一条边就会有一条增广路,所以一条增广路的花费是O(n)O(n)O(n)的,所以是O(n2m)O(n^2m)O(n2m)。当然,至于大众写法,我不会分析QMQ。

放上一张评测图吧:

事实上,如果你能想到有什么方法可以使得一条边经过完之后绝对废除,且不会影响h[ed]h[ed]h[ed]单调递增的性质,你就自创了O(nm)O(nm)O(nm)的算法,当然,这很难。现在虽然已经有nm算法,但是太难懂了

参考资料

EK时间复杂度的分析

一篇写得不错得最大流博客,术语很齐全

论如何卡掉Dinic(我没看懂)
咕咕讨论,Zadeh Construction是个什么东西

二分图匹配Dinic重拳出击

各种算法的时间复杂度以及HLPP的讲解

Dinic之神

最大流的正确性

各种算法的时间复杂度

算法导论爷Orz

学习HLPP。(感觉这辈子都看不懂时间复杂度得证明)

卡掉Dinic。误

证明二分图中Dinic的时间复杂度。

EK时间复杂度证明中提到的例子。

网络流重制版:最大流Dinic,以及EK、Dinic时间复杂度的证明(含坑)相关推荐

  1. dinic算法 c 语言,网络流入门—用于最大流的Dinic算法

    "网络流博大精深"-sideman语 一个基本的网络流问题 最早知道网络流的内容便是最大流问题,最大流问题很好理解: 解释一定要通俗! 如右图所示,有一个管道系统,节点{1,2,3 ...

  2. 最大流算法模板:EK和dinic算法

    最大流算法模板:EK和dinic算法 一.EK算法模板 #include<iostream> #include<queue> using namespace std; cons ...

  3. 小米android系统分享,重温的手机分享 篇一:90hz高刷?45w快充?双扬声器?你不知道的酷安重制版小米9魔改教程...

    重温的手机分享 篇一:90hz高刷?45w快充?双扬声器?你不知道的酷安重制版小米9魔改教程 2021-05-14 14:47:30 6点赞 13收藏 10评论 大家上午好啊,我是重温,有幸能拿到这款 ...

  4. 红警1开源放出源码,4K高清重制版登顶Steam畅销榜!网友:青春回来了

    大数据文摘出品 作者:笪洁琼 "Sir,yes sir." "You've got no place to hide!" "You'll be a s ...

  5. 无路可逃java攻略_生化危机2重制版无路可逃怎么过_100丧尸模式幽灵生还者无路可逃流程攻略_3DM单机...

    <生化危机2:重制版>的幽灵生还者DLC中,当玩家通关了前面的三章之后就能解锁第四章--无路可逃,这个关卡需要解决100个丧尸,难度十分之高,下面小编就为大家带来一篇"lu_mk ...

  6. 地图上制作线路的动画_魔兽争霸重制版不只是表面上这么简单,新版编辑器制作地图更容易...

    对于许多玩家来说,魔兽争霸绝对是一款信仰级别的游戏,它曾经陪伴我们度过了许多难忘的美好时光,但是对于许多地图开发制作者来说,这个游戏更像是自己的信仰.魔兽一个游戏所衍生出的RPG地图,让许多玩家从中收 ...

  7. 《最终幻想7 重制版》DEMO体验:讨论ATB战斗系统的一些问题

    仅基于DEMO,正式版让我们共同期待 刚刚通关,我个人对这个DEMO的评价还是比较高的,整个玩下来流程不长,大概半小时到四十分钟,但是刚刚好,节选选的相当合适,在一个小情节里把该展示的都给玩家展示了, ...

  8. 蒂法html5游戏,《最终幻想7:重制版》大量新截图:蒂法招式/支线任务

    史克威尔日前为<最终幻想7:重制版>公布了大量的新截图和艺设图,以及新情报. 请输入图片描述 首先是两名熟悉的角色赤红十三和宝条博士.之后还有一些预购特典召唤兽,陆行鸟.宝石兽.仙人掌等等 ...

  9. ff7重制版青魔法_狂父重制版发布+妖精的尾巴首次打折¥244+最终幻想4解锁国区新增中文...

    #支持我们独立创作内容,来我们店铺购买游戏吧!苹果手机用户请点击文章底部原文链接,访问店铺.# 心理恐怖游戏<狂父 Mad Father>的重制版正式发布,国区价格从¥21上调至¥37,之 ...

最新文章

  1. 判断一个IP区间(或IP)是否被另一个IP区间所包含
  2. 【阿里云课程】模型设计之动态网络,原理与设计
  3. 介绍MyBatis代码生成网站(四) --- 继承基类
  4. 在EXT中前后台传数据的方式
  5. CSS 多浏览器兼容又一方案
  6. zookeeper基本操作(常用命令)
  7. [JSOI2010] 满汉全席
  8. k8s mysql数据同步_K8s——MySQL实现数据持久化
  9. .NET Compact Framework.代码展开ComboBox下拉选项
  10. java中sort的cmp_快速排序(cmp函数详解)
  11. ORACLE OUI 中断 do not have sufficient permissions /u01/app/oraInventory
  12. SqlDataReader.GetFloat出错,类型无法转换
  13. HashMap与LinkedHashMap的结构对比
  14. pytorch版crnn网络框架
  15. Java使用蔡勒公式快速计算某一天是星期几
  16. 从零开始搭建ABP框架(Asp .Net Boilerplate)+Oracle(11 g)
  17. h5页面禁用手机识别
  18. 什么是信用违约互换(信用违约掉期) - 债券市场中最常见的信用衍生品
  19. JNI 静态注册和动态注册
  20. Oracle数据优化,超级全

热门文章

  1. 导航上显示某个地点已关闭什么意思_牢记!想要玩好iPhone手机,6个关闭、4个开启...
  2. (十)play之yabe项目【测试】
  3. Transformer11
  4. 极路由4pro交叉编译c、c++的代码
  5. Java学习路线:day7 数组2
  6. 如何简单制作自己想要的GeoJSON地理信息文件
  7. PHP输出一段励志的话,50句励志的话_励志的句子:相信自己总会好的
  8. Duplicated tag: ‘mirrors‘
  9. virtualenv虚拟环境安装
  10. 如何做好资源一般的旅游景区开发经营和旅游规划?