网络流引入

网络流

基本定义

首先我们定义一个名为“网络”的有向图,并将其边权命名为“容量”,额外的,我们有一个“源点”和一个“汇点”

,顾名思义,就像水流或电流,也具有它们的性质。如果把网络想象成一个管道网络,那流就是其中流动的水。每条边上的流不能超过它的容量,并且对于除了源点和汇点外的所有点(即中继点),流入的流量都等于流出的流量。

问题

给定一个网络,求其最大流?

分析

我们先引入一个概念,名为增广路。其定义为由源点到汇点的一条路径,且残余容量大于零。为何说是残余容量呢?因为我们每找到一条增广路,增广路上的每条边就要减去流量,故称残余容量。

而我们经过一次次增广(寻找增广路),在将所有流量相加就可求出最大流了。

但是!

img

比如这张图,我们如果先增广 1->2->3->4这条路径的话,我们将得到 1 1 1 的流,而显然,如果我们分别增广 1->3->4 和 1->2->4这两条路径的话,我们可以得到 2 2 2 的流。

而这怎么解决的呢?

我们可以对每一条边建立一条反边,如下图:

img

然后这时候我们在这个网络上进行增广,并将流量加给反边,原本的问题我们惊奇的发现可以解决了!

img

那我们来理解一下,残余流量就是容量减去流量,而我们把流量加到了反边上,所以我们在使用反边时,就给正边加上流量,也就相当于没有用这条边——即为「反悔」

FF算法

讲完解法,我们看算法

首先便是FF算法,这个算法通过DFS查找增广路,上代码:

int n, m, S, T; // S是源点,T是汇点
bool vis[N];
int dfs(int u = S, int limit = INF){
    if(u == t)
        return limit; // 到达终点,返回这条增广路的流量
    vis[p] = true;
    for(int i = head[p]; ~i; i = edge[i].nxt){
        int to = edge[i].to, vol = edge[i].w, c;
        // 返回的条件是残余容量大于0、未访问过该点且接下来可以达到终点(递归地实现)
        // 传递下去的流量是边的容量与当前流量中的较小值
        if(vol > 0 && !vis[to] && (c = dfs(to, min(vol, flow))) != -1){
            edge[i].w -= c;
            edge[i ^ 1].w += c;
            // 这是链式前向星取反向边的一种简易的方法
            // 建图时要把第一条边的编号设置为偶数,且要保证反向边紧接着正向边建立
            return c;
        }
    }
    return -1; // 无法到达终点
}
inline int FF(){
    int ans = 0, c;
    while((c = dfs()) != -1){
        memset(vis, 0, sizeof(vis));
        ans += c;
    }
    return ans;
}

用dfs实现的FF算法时间复杂度上界是 O ( e f ) O(ef) O(ef),其中 e e e 为边数, f f f 为最大流

(因复杂度问题一般不用)

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值