网络流是一个博大精深的OI类别
今天浅显地理解了一下网络流,做一下笔记
1.网络:是一个有向图,每个边有一个容量c(x,y),每条边也会有一个可行的流量f(x,y),这个f被称为流函数
图中有一个源点S,不断往外流水,只流不入,汇点T则相反。其他的点流入的量等于流出的量。
流/增广路:一条从S 出发,能到达T且经过的边的流函数最小值>0的路径,称为一个流/增广路
网络流:顾名思义,和水流类似,就是在一张网络上流水,每条边好似一个管道。
2.三大定律:
①容量限制:f(x,y)<=c(x,y) 每条边不能超过边的容量。这限制了网络一定有一个最大的总流量
②斜对称:f(x,y)=-f(y,x) 一条边相当于是一个正边和反边组成,从x往y流flow,相当于从y往回流-flow
这一点其实是人为定义的,原图中一般也不画出,但是相当关键,也是可以用dinic,EK等算法直接求最大流的“反悔回流”的条件
这个反边是一定要建的。不要忘了。
③流量守恒。除了源点,汇点之外,每个点流入的总流量,一定等于流出的总流量。
这一点也是很重要的基础,为最大流求法和模型构建提供了成立的条件。
之后边的容量保留的是边的剩余流量,正向减去,反向会加上。初始正向是最大容量,反向是0
最大流
之前已经说了,每个点有一个流入的和流出的量。
定义,一个网络的总流量为:∑f(s,v)即从源点流出的总量。
满足三大定律的流函数有很多,其中最多的总流量称为这个网络的最大流。
网络流的扩展变形都是建立在最大流基础上的。
算法:
①Edmonds-Karp(EK)利用bfs每次找到一条增广路增广。
(留坑,学完费用流再补)
upda:
EK每次bfs找到一条 增广路,然后对这条增广路进行流量改变。
记录一个incf[x],表示,进入x的流量。
要记录一个pre[x],表示点x是通过哪条边转移过来的。
一般能处理10^3~10^4的网络
②Dinic算法
发现EK每次最多只找到一条增广路,效率不是很高
Dinic bfs搜出分层图,dfs多路增广,效率就很高了。
一般能处理10^4~10^5的网络。
模板:
luoguP3376 【模板】网络最大流
#include<bits/stdc++.h> using namespace std; const int N=10000+7; const int M=100000+7; const int inf=0x3f3f3f3f; int n,m; struct node{ int nxt,to; int w; }e[2*M]; int hd[N],cnt=1;//从2开始编号,i^1就是反边编号 int s,t; void add(int x,int y,int z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].w=z; hd[x]=cnt; } int d[N]; queue<int>q; bool bfs(){//bfs找分层图 while(!q.empty()) q.pop(); memset(d,0,sizeof d); d[s]=1; q.push(s); while(!q.empty()){ int x=q.front();q.pop(); for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!d[y]&&e[i].w){ d[y]=d[x]+1; q.push(y); if(y==t) return 1; } } } return 0; } int dfs(int x,int flow){//dfs多路增广 if(x==t) return flow; int rest=flow; for(int i=hd[x];i&&rest;i=e[i].nxt){ int y=e[i].to; if(d[y]==d[x]+1&&e[i].w){ int k=dfs(y,min(rest,e[i].w)); if(!k) d[y]=0; rest-=k; e[i].w-=k; e[i^1].w+=k; } } return flow-rest; } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); int x,y,z; for(int i=1;i<=m;i++){//建边 scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,0);//反边起初容量是0 } int maxflow=0; int flow; while(bfs()) while(flow=dfs(s,inf)) maxflow+=flow; printf("%d",maxflow); return 0; }