首先是一些关于网络流的术语:

源点:即图的起点。
汇点:即图的终点。
容量:有向边(u,v)允许通过的最大流量。
增广路:一条合法的从源点流向汇点的路径。

网络流问题是在图上进行解决的,我们通常可以将问题转化为:

给定一个有向图,每条边有一个容量,有两个点被标记做了源点与汇点,你要确定尽量多的从源点到汇点的路径,每条边被经过的次数不得超过它的容量。我们将一个合法解称作一个流,一条边被经过的次数称作其流量,最终流的总和称作整个流的流量。

我们的限制转化为:

每条边被经过的次数不得超过它的容量->每条边的流量不超过其流量,流由若干从源点到汇点的路径组成->除源点和汇点外,对于每个点,流入它的流量和等于从它流出的流量和。最大化整个流的流量->最大化从源点流出的流量。

前两个条件分别被称为容量限制和流量平衡。

可以显然地想到一个(不正确的)解法,即不停地找一条任意的路径并流过去。

如何做到"可以反悔"呢?

减少一条边上k的流量,相当于反向流过来k的流量。这个还是比较显然的。假设你把一些货物从a地运到b地,后来你发现运错了,那就再运回来就行了。定义一条边的残量,是指它还能流多少流量(即容量减去当前流量)。

刚刚的反思告诉我们:

在一条边流过去之后,我们需要反过来建一条边。如果边u流过去了一些流量,那么我们需要建一条反过来的边,比如叫做v。v的残量即为u当前的流量。沿着v流一些流量,对应到原问题中相当于在u这条边上少流了一些流量。这就是网络流最大流的核心思想。

FF方法
(1)在残量网络上找到一条从源点到汇点的道路(称为"增广路")
(2)取增广路上残量最小值v
(3)将答案加上v
(4)将增广路上所有边的残量减去v,而它们的反向边的残量加上v。
重复这个过程直到找不到增广路,就可以求出最大流。
步骤4中,一条边的反向边的反向边即为这条边本身(即它们两个互为反向边)。
首先这个算法是不会死循环的,因为每次增广都导致流量增加(并且增加的是整数),而流量有一个客观存在的最大值,所以它必定结束。
由于他没有指定存在多条增广路的时候选哪一条,所以我们先考虑最简单的情况:随便找一条。
经过实践,我们可以想到只增广最短路径。

然后讲一下EK算法:

EK算法

是FF的一种实现:每次寻找增广路增广。可以证明其复杂度是O((m^2)*n)的。

首先我们考虑该如何建反向边:

我们选择用邻接表存边(邻接矩阵受数据范围限制,一般无法开心的使用),那么反向边的编号该如何处理,才能使这两条边相互关联起来?

答案之一就是异或(不会请自行百科):

0^1 = 1, 1^1 = 0;

2^1 = 3, 3^1 = 2;

4^1 = 5, 5^1 = 4;

于是我们发现,异或1这一操作,可以将相邻的两个整数关联起来(偶数在前,奇数在后),然后我们可以选择从零开始存在,完美解决存边问题。

至于算法的核心思想,其实就是FF方法,只是进行了具体实现。

来一波核心代码:

int EK(int s, int t)    //s为源点,为汇点
{
int DIS = ; //DIS用来记每次BFS找到的增广路的最大流量
int ans = ;     //ans用来记最终答案
while ((DIS = BFS(s, t)) != -)
{
int cur = t;
while (cur != s)//根据BFS得出的前驱(pre)数组遍历路径,更改容量
{
e[pre[cur]].w -= DIS;
e[pre[cur] ^ ].w += DIS;
cur = e[pre[cur]].from;
}
ans += DIS;
}
return ans; //返回答案
}

BFS的任务是得出的前驱(pre)数组(就是一条增广路),并记录到每个点为止的最大流量(flow)数组。

BFS找到一条增广路就应该返回。且不走重点(否则复杂度将无法承受)。

完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <cctype> using namespace std; const int MAXN = ;
const int INF = ; struct Edge {
int from;
int to;
int w;
int next;
}e[MAXN << ]; int n, m, s, t, x, y, z;
int cnt = -;
int f[MAXN];
int pre[MAXN];
int flow[MAXN]; queue <int> q; int min(int a, int b)
{
if (a <= b) return a;
else return b;
} int read()
{
int x = ;
int k = ;
char c = getchar(); while (!isdigit(c))
if (c == '-') k = -, c = getchar();
else c = getchar(); while (isdigit(c))
x = x * + c - '',
c = getchar();
return k * x;
} int BFS(int s, int t)
{
while (!q.empty()) q.pop();
memset(pre, -, sizeof(pre));
memset(flow, 0x7f, sizeof(flow));
pre[s] = ;
q.push(s);
flow[s] = INF;
while (!q.empty())
{
int cur = q.front();
q.pop(); if (cur == t) break; for (int i = f[cur]; i != -; i = e[i].next)
{
if (pre[e[i].to] == - && e[i].w > )
{
pre[e[i].to] = i;
flow[e[i].to] = min(flow[cur], e[i].w);
q.push(e[i].to);
}
} }
if (pre[t] == -) return -;
return flow[t];
} void Add_edge(int from, int to, int w)
{
++cnt;
e[cnt].from = from;
e[cnt].to = to;
e[cnt].w = w;
e[cnt].next = f[from];
f[from] = cnt;
} int EK(int s, int t)
{
int DIS = ;
int ans = ;
while ((DIS = BFS(s, t)) != -)
{
int cur = t;
while (cur != s)
{
e[pre[cur]].w -= DIS;
e[pre[cur] ^ ].w += DIS;
cur = e[pre[cur]].from;
}
ans += DIS;
}
return ans;
} int main()
{
n = read();
m = read();
s = read();
t = read(); memset(f, -, sizeof(f)); for (int i = ; i <= m; ++i)
{
x = read();
y = read();
z = read();
Add_edge(x, y, z);
Add_edge(y, x, );
} cout << EK(s, t);
char c = getchar();
return ;
}

网络流(一)——Edmonds Karp算法的更多相关文章

  1. 最大流算法之Ford-Fulkerson算法与Edmonds–Karp算法

    引子 曾经很多次看过最大流的模板,基础概念什么的也看了很多遍.也曾经用过强者同学的板子,然而却一直不会网络流.虽然曾经尝试过写,然而即使最简单的一种算法也没有写成功过,然后对着强者大神的代码一点一点的 ...

  2. 网络流-最大流问题 ISAP 算法解释(转自Renfei Song's Blog)

    网络流-最大流问题 ISAP 算法解释 August 7, 2013 / 编程指南 ISAP 是图论求最大流的算法之一,它很好的平衡了运行时间和程序复杂度之间的关系,因此非常常用. 约定 我们使用邻接 ...

  3. HDU3549 Flow Problem(网络流增广路算法)

    题目链接. 分析: 网络流增广路算法模板题.http://www.cnblogs.com/tanhehe/p/3234248.html AC代码: #include <iostream> ...

  4. hdu2389二分图之Hopcroft Karp算法

    You're giving a party in the garden of your villa by the sea. The party is a huge success, and every ...

  5. Edmonds 开花算法

    Edmonds 开花算法 input: 图G,匹配M,未饱和点u idea: 查找从 u 開始的 M-交错路径.对每一个顶点记录父亲节点. 发现花朵.则收缩. 维护 S 和 T.S 表示沿着已经饱和的 ...

  6. POJ 2455 网络流 基础题 二分+网络流 dicnic 以及 sap算法

    Secret Milking Machine Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8189   Accepted: ...

  7. 网络流入门--最大流算法Dicnic 算法

    感谢WHD的大力支持 最早知道网络流的内容便是最大流问题,最大流问题很好理解: 解释一定要通俗! 如右图所示,有一个管道系统,节点{1,2,3,4},有向管道{A,B,C,D,E},即有向图一张.  ...

  8. 网络流的$\mathfrak{Dinic}$算法

    网络流想必大家都知道,在这不过多赘述.网络流中有一类问题是让你求最大流,关于这个问题,许多计算机学家给出了许多不同的算法,在这里--正如标题所说--我们只介绍其中的一种--\(\tt{Dinic}\) ...

  9. 网络流之最大流算法(EK算法和Dinc算法)

    最大流 网络流的定义: 在一个网络(有流量)中有两个特殊的点,一个是网络的源点(s),流量只出不进,一个是网络的汇点(t),流量只进不出. 最大流:就是求s-->t的最大流量 假设 u,v 两个 ...

随机推荐

  1. 这几道Python面试题,稳准狠,Python面试题No15

    必须放一个表情包,太魔性了! 第1题: 修改以下Python代码,使得下面的代码调用类A的show方法? 原始代码 class A(object): def run(self): print(&quo ...

  2. layui实现下拉分类多级

    Layui tree 下拉菜单树   1.效果: 2.html  代码: <!DOCTYPE html> <html> <head> <meta charse ...

  3. 8.聚集函数 ---SQL

    一.AVG()函数 A VG()通过对表中行数计数并计算其列值之和,求得该列的平均值.A VG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值. 警告:只用于单个列 AVG()只能用来确 ...

  4. CF914D Bash and a Tough Math Puzzle 线段树+gcd??奇怪而精妙

    嗯~~,好题... 用线段树维护区间gcd,按如下法则递归:(记题目中猜测的那个数为x,改动次数为tot) 1.若子区间的gcd是x的倍数,不递归: 2.若子区间的gcd是x的倍数,且没有递归到叶子结 ...

  5. Luogu P3166 [CQOI2014]数三角形 组合数学

    好题鸭.. 不好直接求三角形个数,那就用全集-补集,转化为求三点共线的数量. 具体求法是求出水平共线数量与竖直共线数量和斜线共线数量. 用排列组合的知识可知为水平和竖直的为$C_n^3$​与$C_m^ ...

  6. Codeforces 1163D(kmp、dp)

    要点 \(dp[i][j][k]\)表示主串已经到第\(i\)位时,\(s\)匹配在\(j\)位.\(t\)匹配在\(k\)位的最大得分 本来就要试填一层循环,如果转移也写在循环里的化复杂度承受不了, ...

  7. node.js安装Oracledb指导文档

    https://community.oracle.com/docs/DOC-931127

  8. vue.js数据绑定语法

    原始高清大图下载 1.数据绑定 html代码: <div id="first" class="first">msg:{{msg}}</div& ...

  9. c#基础3-方法的重载静态和非静态,字段属性,方法

    方法的重载概念:方法的重载指的就是方法的名称相同给,但是参数不同.参数不同,分为两种情况1).如果参数的个数相同,那么参数的类型就不能相同.2).如果参数的类型相同,那么参数的个数就不能相同.***方 ...

  10. TCP的三次握手以及TCP状态转换图详解

    今天来讨论一下TCP的三次握手以及TCP的状态转换图.首先发一个三次握手的流程图如下: 圖 2.4-3.三向交握之封包连接模式A:封包发起当用戶端想要对服务器端发起连接时,就必須要送出一個要求连线的封 ...