先推荐一个讲网络流的博客,我的网络流知识均吸收于此   传送门

EdmondsKarp算法基本思想:从起点到终点进行bfs,只要存在路,说明存在增广路径,则取这部分路 权值最小的一部分,即为增广路径(也就是这一部分路的最大流量)。然后将这条路上的正向权值都减去min,反向权值都加上min(即,m[i][j]-min,m[j][i]+min,为什么等会再解释)。然后重复此操作,最终就得到了最大流。

先上模板(也是取自于刚才的博客,真的写的很精简很好懂)。

邻接矩阵版本。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = ;
const int MAX_INT = (( << ) - ); int n; // 图中点的数目
int pre[MAXN]; // 从 s - t 中的一个可行流中, 节点 i 的前序节点为 Pre[i];
bool vis[MAXN]; // 标记一个点是否被访问过
int mp[MAXN][MAXN]; // 记录图信息 bool bfs(int s, int t){
queue <int> que;
memset(vis, , sizeof(vis));
memset(pre, -, sizeof(pre));
pre[s] = s;
vis[s] = true;
que.push(s);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = ; i <= n; i++){
if(mp[u][i] && !vis[i]){
pre[i] = u;
vis[i] = true;
if(i == t) return true;
que.push(i);
}
}
}
return false;
} int EK(int s, int t){
int ans = ;
while(bfs(s, t)){
int mi = MAX_INT;
for(int i = t; i != s; i = pre[i]){
mi = min(mi, mp[pre[i]][i]);
}
for(int i = t; i != s; i = pre[i]){
mp[pre[i]][i] -= mi;
mp[i][pre[i]] += mi;
}
ans += mi;
}
return ans;
}
 

这部分代码唯一不好理解的就是,思考了很久,终于在跑步时想通了这部分代码的原理。

for(int i = t; i != s; i = pre[i]){
mp[pre[i]][i] -= mi;
mp[i][pre[i]] += mi;
}

mp[pre[i]][i]-mi好理解,因为已经流过了嘛,那为什么有mp[i][pre[i]]+=mi呢?让我们看一个具体的例子。

按照我们的做法,第一次得到了1-2-4-6这条路径,会发现最大流此时是2,然后将路径上的值全部减去2,然后得到的图是这样的:

如果只减掉了正向的路径的权值,这个图就不连通了,也就是答案是2,然而其实答案是3,为什么呢,让我们加上反向的权值看一下:

红色的就是反向的权值,然后再进行上述过程,会发现还有一条 1-3-4-2-5-6的路径,权值为1,所以答案是2+1=3.(这个答案大家可以根据第一幅图自己想一下,会发现就是3,好神奇有木有?)

那为什么这么神奇呢,我的理解是,反向路径代表了,这条路曾经通了多少水,比如2-4这条就通2滴水,然后在第二条路径中通过的这1滴水,其实就是2号路径跟1号路径说,你原本在2-4这条路上有两滴水,现在分一滴到我想去的地方,然后我的水去你想去的地方,这样我们都能实现目标。(想一下是不是这么回事)。

解决了这个难题,那EK算法的模板你就理解啦!

接下来送上邻接表的版本。

const int MAXN = ;
const int MAX_INT = ( << ); struct Edge{
int v, nxt, w;
}; struct Node{
int v, id;
}; int n, m, ecnt;
bool vis[MAXN];
int head[MAXN];
Node pre[MAXN];
Edge edge[MAXN]; void init(){
ecnt = ;
memset(edge, , sizeof(edge));
memset(head, -, sizeof(head));
} void addEdge(int u, int v, int w){
edge[ecnt].v = v;
edge[ecnt].w = w;
edge[ecnt].nxt = head[u];
head[u] = ecnt++;
} bool bfs(int s, int t){
queue <int> que;
memset(vis, , sizeof(vis));
memset(pre, -, sizeof(pre));
pre[s].v = s;
vis[s] = true;
que.push(s);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = head[u]; i + ; i = edge[i].nxt){
int v = edge[i].v;
if(!vis[v] && edge[i].w){
pre[v].v = u;
pre[v].id = i;
vis[v] = true;
if(v == t) return true;
que.push(v);
}
}
}
return false;
} int EK(int s, int t){
int ans = ;
while(bfs(s, t)){
int mi = MAX_INT;
for(int i = t; i != s; i = pre[i].v){
mi = min(mi, edge[pre[i].id].w);
}
for(int i = t; i != s; i = pre[i].v){
edge[pre[i].id].w -= mi;
edge[pre[i].id ^ ].w += mi;
}
ans += mi;
}
return ans;
} // 加边
addEdge(u, v, w);
addEdge(v, u, );
// 调用
int ans = EK(s, t);

网络流EdmondsKarp算法模板理解的更多相关文章

  1. POJ 1273 Drainage Ditches(网络流dinic算法模板)

    POJ 1273给出M条边,N个点,求源点1到汇点N的最大流量. 本文主要就是附上dinic的模板,供以后参考. #include <iostream> #include <stdi ...

  2. 网络流Edmonds-Karp算法入门

    今天自习课没事干,看书自学了一下网络流中的EK算法.(求最大流) 设s为源点,t为汇点,C为容量矩阵,F为流量矩阵,f为最大流量. 1.初始化F,f 2.用BFS在残量网络中找到一条从s到t的最短增广 ...

  3. 网络流EK算法模板

    \(EK\)算法的思想就是每一次找一条增广路进行增广. 注意几个点: 存图时\(head\)数组要设为\(-1\). 存图的代码是这样的: inline void add(int u, int v, ...

  4. POJ 3281 [网络流dinic算法模板]

    题意: 农场主有f种食物,d种饮料,n头牛. 接下来的n行每行第一个数代表第i头牛喜欢吃的食物数量,和第i头牛喜欢喝的饮料数目. 接下来分别是喜欢的食物和饮料的编号. 求解:农场主最多能保证几头牛同时 ...

  5. 网络流 EK算法模板。

    这篇博客讲得很好 #include<queue> #include<stdio.h> #include<string.h> using namespace std; ...

  6. 网络流Dinic算法模板 POJ1273

    这就是以后我的板子啦~~~ #include <queue> #include <cstdio> #include <cstring> #include <a ...

  7. HDU1532最大流 Edmonds-Karp,Dinic算法 模板

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...

  8. ACM - 图论- 网络流 - 算法模板

    \(EK\) 算法模板 #include <iostream> #include <queue> #include<string.h> using namespac ...

  9. POJ 1273 - Drainage Ditches - [最大流模板题] - [EK算法模板][Dinic算法模板 - 邻接表型]

    题目链接:http://poj.org/problem?id=1273 Time Limit: 1000MS Memory Limit: 10000K Description Every time i ...

随机推荐

  1. Github中README.md换行

    两个以上的空格,然后回车.我date

  2. hbase性能调优(转载)

    一.服务端调优 1.参数配置 1).hbase.regionserver.handler.count:该设置决定了处理RPC的线程数量,默认值是10,通常可以调大,比如:150,当请求内容很大(上MB ...

  3. 之前在不网站看到过关于css的一些例子 今天自己也写了一个css特效

    下面是代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <tit ...

  4. 前端基础 之 BOM和DOM

    浏览目录 背景 BOM window对象 window的子对象 DOM HTML DOM树 查找标签 节点操作 事件 一.背景 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些 ...

  5. python---pyspider,报错?

    conf.json文件内容如下: { "message_queue": "redis://127.0.0.1:6379/15", "webui&quo ...

  6. C++面试笔记--面向对象

    说到面向对象,大家第一反应应该就是它的三大特性:封装性.继承性和多态性.那么我们先简单的了解一下这三大特性: (1)封装性:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的 ...

  7. C++面试笔记--指针和引用

    面试一:指针与引用的区别? 答案: (1)非空区别.在任何情况下都不能使用指向空值的引用.因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针 ...

  8. Linux文件概念

    大多数资源,Linux都是以文件的方式来访问. Linux系统上的文件部分类型说明 –普通文件. •就是储存到磁盘上大家所操作的各种数据文件: –管道文件. •是一个从一端发送数据,从另一端接收数据的 ...

  9. C# 使用SendMessage 函数

    在C#中,程序采用了的驱动采用了事件驱动而不是原来的消息驱动,虽然.net框架提供的事件已经十分丰富,但是在以前的系统中定义了丰富的消息对系统的编程提供了方便的实现方法,因此在C#中使用消息有时候还是 ...

  10. 【C#】 WebApi 路由机制剖析

    C#进阶系列——WebApi 路由机制剖析:你准备好了吗? 转自:https://blog.csdn.net/wulex/article/details/71601478 2017年05月11日 10 ...