最大流问题标号法_网络流的最大流入门

网络流(network-flows)是一种类比水流的解决问题方法,与线性规划密切相关。网络流的理论和应用在不断发展。而我们今天要讲的就是网络流里的一种常见问题——最大流问题。

最大流问题(maximum flow problem),一种组合最优化问题,就是要讨论如何充分利用装置的能力,使得运输的流量最大,以取得最好的效果。求最大流的标号算法最早由福特和福克逊与与1956年提出,20世纪50年代福特(Ford)、(Fulkerson)建立的“网络流理论”,是网络应用的重要组成成分。

再解决这个问题前,我们要先弄懂一些定义

a457fe33a124d0efbeb7094a43f99215.png

网络流图是一张只有一个源点和汇点的有向图,而最大流就是求源点到汇点间的最大水流量,下图的问题就是一个最基本,经典的最大流问题

e1fa4b375925a7a676d69db86e11483a.png

二.流量,容量和可行流

对于弧(u,v)来说,流量就是其上流过的水量(我们通常用f(u,v)表示),而容量就是其上可流过的最大水量(我们通常用c(u,v)表示),只要满足f(u,v)<=c(u,v),我们就称流量f(u,v)是可行流(对于最大流问题而言,所有管道上的流量必须都是可行流)。

三.增广路

4e60ec20ba9ee23c7d8c0a9eb946b97b.png

如果一条路上的所有边均满足:

正向边: f(u,v)< c(u,v) ——– 反向边:f(u,v)> 0

假如有这么一条路,这条路从源点开始一直一段一段的连到了汇点,并且,这条路上的每一段都满足流量

则我们称这条路径为一条增广路径,简称增广路。

好了,弄懂了一些定义,接下来就可以介绍著名的Ford-Fulkerson算法了。

8335943e20c2e2efea7e2f9489de08c0.png

如图所示,如果我们每次都找出一条增广路,只要这条增广路经过汇点,那说明此时水流还可以增加,增加的量为d(d=min(d,c(u,v)-f(u,v))或d=min(d,f(u,v)))。

我们可以这样理解:对于每一条正向边,他能添加的最大水流为c(u,v)-f(u,v)。而对于反向边来说,当正向边上的水流增多时,反向边自身的反向水流会减少,而其能减少的最多水量为f(u,v)。由于要保证添加水流之后,所有的f(u,v)都是可行流,所以我们取最小值。

增加之后,我们要更新流量,每条正向边+d,每条反向边-d即可。

既然这样,我们的思路就是:

1.找出一条增广路径 ——2.修改其上点的值——3.继续重复1,直至找不出增广路。则此时源点的汇出量即为所求的最大流。

0332ea20b722ae49ec243c3367038f10.png
ea7c884df8c49709c50da1edc1b053f1.png
3a38665909e00718de60c5402c78d440.png
3a38665909e00718de60c5402c78d440.png
30bc0e2b6e43b89f073f657acf6e8822.png

那么上代码:

#include#include#define maxn 1200#define INF 2e9using namespace std;int i,j,k,n,m,h,t,tot,ans,st,en;struct node{    int c,f;}edge[maxn][maxn];int flag[maxn],pre[maxn],alpha[maxn],q[maxn],v;int read(){    char c;int x;while(c=getchar(),c'9');x=c-'0';    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;}void bfs(){    memset(flag,0xff,sizeof(flag));memset(pre,0xff,sizeof(pre));memset(alpha,0xff,sizeof(alpha));    flag[st]=0;pre[st]=0;alpha[st]=INF;h=0,t=1;q[t]=st;    while(h0){                    flag[i]=0;pre[i]=-v;alpha[i]=min(alpha[v],edge[i][v].f);q[++t]=i;                }            }        }        flag[v]=1;    }}void Ford_Fulkerson(){    while(1){        bfs();        if(alpha[en]==0||flag[en]==-1){            break;        }        int k1=en,k2=abs(pre[k1]);int a=alpha[en];        while(1){            if(edge[k2][k1].c
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值