网络流初步
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
这大概就是基础网络流知识了,关于上下界网络流的学习就看这里吧。
以上是关于网络流初步的主要内容,如果未能解决你的问题,请参考以下文章