网络流初步:<最大流>——核心(增广路算法)
终于开始接触网络流了;
网络流到底是个虾米东东,用比较学术的话说,就是
一个有向图 G=(V,E);
有两个特别的点:源点s、汇点t;
图中每条边(u,v)∈E,有一个非负值的容量C(u,v)
记为 G=(V,E,C)
网络三要素:点、边、容量
用我的其中,最不好理解的就是容量和流量,对于初学者,应该这样理解,每条边有c表示最多能运送多少货物,而流量f表示最多当前运送的货物多少。
这样要好点嘿嘿。
解决了网络流之后,我们就要想,最大流。
what is最大流
在我们拥有网络流的概念后,我们想啊,最大流,就是起点到终点的最大流量。
其中,在最大流的问题中,我们要满足三个条件,1:容量限制:f(u,v)<c(u,v);
2:斜对称性:(f(u,v)=-f(v,u))在后面的运用中,我们叫做一旦有物品从u运到v,那么则肯定有一个可退流从v运到u
3:流量平衡:简单来说就是除了源点和汇点,没有其他点可以保存货物。显然f(s,u)==f(v,t);
既然这样,我们的目的就是使f(s,u)和f(v,t)最大
怎么使目标最大化呢,现在,就要介绍增广路算法,这非常重要:可以说怎个网络流都必须有增广路算法的影子。
好,我们先想,如果一个网络流还有一条增广路,按照我们的理解,那么这条路上所有的弧都可以让x个货物通过,那么一定不是最大流
比如现在;
还存在S--A---C---T的可增广路,那么就一定不是最大流
但是反过来,如果一个网络没有增广路,那么他一定是最大流吗,有的人说是的,当然啦。但是请看
这也是一个没有增广路的网络,但是,他是最大流吗。
我们发现,还可以这样。orz
所以,刚才那个最多算一个极大流,而不是最大流(什么虾米东东)
也就是说从B出发,本来有两条路可以走的,但是B却走了错误的路,所以它成功地把C---T给堵住了。
我们又称之为“阻塞流”。也就是说一个错误的路让其他可行流被阻塞了。
怎么改进呢,反正B出发只有两条路,都要试一试对吧,既然上面那条是阻塞流。
那下面这条呢,试一试不就知道了,那我们就把B--c压回去,走另外一条路。
现在。就是这样
但是,这肿么实现呢,如果模拟将流压回去,忒难了点吧。
所以,我们引入了反向弧,借助他来推流。
增广路径(可改进路径)的定义
若P是网络中连结源点s和汇点t的一条路,我们定义路的方向是从s到t,则路上的弧有两种:
l 前向弧---弧的方向与路的方向一致。前向弧的全体记为P+;
l 后向弧---弧的方向与路的方向相反。后向弧的全体记为P-;
设F是一个可行流,P 是从s到t的一条路,若P满足下列条件:
在P+的所有前向弧(u,v)上,0≦f(u,v) < C(u,v);
在P-的所有后向弧(u,v)上,0<f(u,v) ≦C(u,v);
则称P是关于可行流F的一条可增广路径。
有点难理解对吧。
但是我们成功了。
现在,网络变成了这个样子。
其实,为什么反向弧退流就对了呢,我认为是这样的,对于一条已经有了可退流的弧,一旦退流,其退流一定可以将退流退完,所以,满足三要素。就一定是对的。
所以,增广路定理就是:当一个残量网路上没有增广路了,那么他一定是最大流。
基于这个定理,我们可以设计出算法。
在一个残量网络上找增广路增广,然后,一旦没有增广路,就一定是最大流。
#include<cstdio> #include<cstring> #include<algorithm> #define N 10000+10 #define M 10000+10 #define inf 1e9; using namespace std; int head[N],arnum=1,used[N],ans,way[M]; struct ss{int next,to,cap;}a[M]; void add(int from,int to,int cap){a[++arnum]=(ss){head[from],to,cap};head[from]=arnum;} void insert(int u,int v,int cap){add(u,v,cap),add(v,u,0);} int n,m,S,T; void work(int step) { int minn=inf; for(int i=1;i<=step;i++) minn=min(minn,a[way[i]].cap); ans+=minn; for(int i=1;i<=step;i++) { a[way[i]].cap-=minn; a[way[i]^1].cap+=minn; } } int dfs(int u,int step) { for(int i=head[u];i;i=a[i].next) { int v=a[i].to; int cap=a[i].cap; if(not used[v] and cap>0) { way[step]=i; used[v]=1; if(v==T){work(step);return 1;} else if(dfs(v,step+1))return 1; } return 0; } } int main() { scanf("%d%d%d%d",&n,&m,&S,&T); int u,v,c; for(int i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&c);insert(u,v,c);} for(;;) { memset(used,0,sizeof(used)); used[S]=1; if(not dfs(S,1))break; } printf("%d",ans); return 0; }
这就是最低级的算法吧O(∩_∩)O哈哈~。
附上代码