网络流——最大流/Ford-Fulkerson算法

图论中一个关键的算法——最大流,很多实际问题都是这个思想,比如最大传输量最大管道流量等等。

根据《数据结构与算法分析》和《挑战程序设计》进行综合整理。

最大流问题

设给定有向图G=(V , E),其边容量为Cv,w。这些容量可以代表通过一个管道的水的流量。图中有两个顶点:一个是s(称为发点or源点),一个是t(称为收点or汇点)。对于任意一条边e∈E,最多有“流”C(e)个单位可以通过。在不是s和t的任一顶点v,总的进入的流必须等于总的发出的流。
而最大流就是确定从s到t可以通过的最大流量。

记每条边对应的实际数据传输量为f(e)。并有0 <= f(e) <= c(e)

分析

首先考虑一个错误算法——贪心算法

  • 1.找一条s到t的只经过f(e) < c(e)的边的路径,这条路径叫做增长通路
  • 2.如果不存在满足条件的路径,则结束算法。否则,沿着该路径尽可能地增加c(e),返回第1步。

然而,这个算法是不确定的,因为我们是随意选择一条从s到t的路径,所以我们不能保证每次选的路径就是最好的选择,所以这个算法是欠缺的。


以此图为例:
这里写图片描述

假使源点s为顶点4,汇点t为顶点5。
1.如果选择路径4->1->3->5,该路径容许2个单位流量通过
2.然后选择路径4->0->2->5,该路径容许2个单位流量通过
3.最后选择路径4->0->3->5,该路径容许1个单位流量通过
这样,算法终止,并且结果就是最大流5。

但是如果我们刚开始直接选择路径4->0->3->5,该路径容许3个单位流量通过,但是一旦这样选择,接下来就没有从s到t的路径可以选择了,即算法终止,然后结果只是3,不是最大流,所以证实了这个算法失败了。

—————————————分割线——————————————–

为了解决这个问题,使得算法有效,我们需要让算法改变它的意向。我们通过将原先得到的流给推回去,而得到新的流。这样将之前的算法修改一下:

  • 1.只利用满足f(e) < c(e)的e或者满足f(e) > 0的e对应的反向边rev(e),寻找一条s到t的路径。
  • 2.如果不存在满足条件的路径,则结束。否则,沿着该路径尽可能地增加流,返回第1步。

所以在刚才的例子中,假使第一步已经选择路径4->0->3->5了,容许3个单位流量通过。
那么我们将从5开始5->3->0->4进行回推,这样在3到0之间有一条新的边(反向边)如图所示。
这里写图片描述

这样我们就又可以选择一条路径从s到t了,即4->1->3->0->2->5,并且容许2个单位的流量通过,这样我们总共就有了5个单位的流量,即最大流。

这个算法名叫Ford-Fulkerson算法

Ford-Fulkerson算法

实现代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1000+10;
const int INF = 1000000000;
struct edge{
    int to, cap, rev;
}; 

vector<edge> G[maxn];
bool used[maxn];
int V,E;

/*向图中增加一条从s到t为cap的边*/
void add_edge(int from, int to, int cap)
{
    G[from].push_back((edge){to, cap, G[to].size()});
    G[to].push_back((edge){from, 0, G[from].size() - 1});
} 

/*通过DFS找增广路*/
int dfs(int v, int t, int f)
{
    if(v == t) return f;
    used[v] = true;
    for(int i=0; i<G[v].size(); i++)
    {
        edge &e = G[v][i];
        if(!used[e.to] && e.cap > 0)
        {
            int d = dfs(e.to, t, min(f, e.cap));
            if(d > 0)
            {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

/*求解从s到t的最大流*/
int max_flow(int s, int t)
{
    int flow = 0;
    for(; ;)
    {
        memset(used, 0, sizeof(used));
        int f = dfs(s, t, INF);
        if(f == 0) return flow;
        flow += f;
    }
}

int main()
{
    scanf("%d%d",&V,&E);
    int s,t,e;
    for(int i=0; i<E; i++)
    {
        scanf("%d%d%d",&s,&t,&e);
        add_edge(s, t, e);  
    }
    printf("%d\n", max_flow(0, V-1));
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值