前几天学了Dinic求最大流,来总结一下。
网络流的概念是很多的。
预备知识:
在一个有向图上选择一个源点,一个汇点,每一条边上都有一个流量上限(以下称为容量),即经过这条边的流量不能超过这个上界,同时,除源点和汇点外,所有点的入流和出流都相等,而源点只有流出的流,汇点只有汇入的流。这样的图叫做网络流。
我们定义:
源点:只有流出去的点
汇点:只有流进来的点
流量:一条边上流过的流量
容量:一条边上可供流过的最大流量
残量:一条边上的容量-流量
算法思想:
网络流的所有算法都是基于一种增广路的思想,下面首先简要的说一下增广路思想,其基本步骤如下:
1.找到一条从源点到汇点的路径,使得路径上任意一条边的残量>0(注意是小于而不是小于等于,这意味着这条边还可以分配流量),这条路径便称为增广路
2.找到这条路径上最小的F[u][v](我们设F[u][v]表示u->v这条边上的残量即剩余流量),下面记为flow
3.将这条路径上的每一条有向边u->v的残量减去flow,同时对于起反向边v->u的残量加上flow(为什么呢?我们下面再讲)
4.重复上述过程,直到找不出增广路,此时我们就找到了最大流
介绍下这个博客,写的十分简洁易懂。本博客内容部分转载自
http://www.cnblogs.com/SYCstudio/p/7260613.html
最小割:通俗一点理解就是最小的费用让A,B两个集合不连通.
最大流最小割定理:最大流=最小割
Dinic基本思想:
1.不断找增广路,即从s-->t的可行路径,用dfs实现。
2.运用了分层图优化,用bfs实现。
3.可以用当前弧优化,即记录每个点已经访问过的边,下次dfs直接从没有访问过的边开始。
例题:网络流24题
推荐题:
飞行员配对方案问题
[USACO4.2]草地排水Drainage Ditches
最大流模板代码:
#include<cstdio> #include<cmath> #include<cstring> #include<queue> using namespace std; const int N=10000+5; const int M=200000+10; const int INF=0x3f3f3f3f; int n,m,s,t,num=1,head[N],dep[N],ans,q[N*3],cur[N]; struct E{ int next,to,dis; }e[M]; void add( int u, int v, int w){ e[++num].to=v; e[num].next=head[u]; e[num].dis=w; head[u]=num; } int dfs( int u, int flow){ if(u==t)return flow; for( int i=cur[u];i;i=e[i].next){//当前弧优化 int v=e[i].to; if(dep[v]==dep[u]+1&&e[i].dis){ int w=dfs(v,min(flow,e[i].dis)); if(w>0){ e[i].dis-=w; e[i^1].dis+=w; return w; } } } return 0; } inline int bfs(){ memset(dep,0,sizeof(dep)); int u,v; dep[s]=1; queue<int>q; q.push(s); while(!q.empty()){ u=q.front(); q.pop(); for( int i=head[u];i;i=e[i].next){ v=e[i].to; if(e[i].dis&&!dep[v]){ dep[v]=dep[u]+1; if(v==t)return 1; q.push(v); } } } return 0; } void Dinic(){ while(bfs()){ for( int i=1;i<=n;i++) cur[i]=head[i];//当前弧优化 ans+=dfs(s,INF); } } int main(){ scanf("%d%d%d%d",&n,&m,&s,&t); for( int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,0); } Dinic(); printf("%d",ans); return 0; }
总结:网络流的最大难度在于建图,这点十分关键,主要是理清楚题意,寻找条件。