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

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. MySQL中的多表插入更新与MS-SQL的对比

    MySQL多表插入: INSERT INTO tdb_goods_cates (cate_name) SELECT goods_cate FROM tdb_goods GROUP BY goods_c ...

  2. Python程序退出方式(sys.exit() os._exit() os.kill() os.popen(...))

    对于如何结束一个Python程序或者用Python操作去结束一个进程等,Python本身给出了好几种方法,而这些方式也存在着一些区别,对相关的几种方法看了并实践了下,同时也记录下. 参考: Pytho ...

  3. SSH框架搭建步骤

    1.创建一个工程2.工程的编码改成utf-83.把jsp的编码也改成utf-84.导入jar包5.建立三个src folder    src            存放源代码    config    ...

  4. ROS探索总结(五)——创建简单的机器人模型smartcar

    前面我们使用的是已有的机器人模型进行仿真,这一节我们将建立一个简单的智能车机器人smartcar,为后面建立复杂机器人打下基础. 一.创建硬件描述包 roscreat-pkg  smartcar_de ...

  5. C++字符串流保存数据

    文件流是以外存文件为输入输出对象的数据流.字符串流是以内存中用户定义的字符数组(字符串)为输入输出对象的. 建立输出字符串流: ostrstream strout(c,sizeof(c));第一个参数 ...

  6. Spring集成MyBatis01 【推荐使用】、springMVC中文乱码和json转换问题

    1 导包 1.1 spring-webmvc : spring框架包(当然里面也包含springmvc) 1.2 mybatis : mybatis框架包 1.3 mybatis-spring : s ...

  7. Boost中实现线程安全

    博客转载自: http://www.cnblogs.com/lvdongjie/p/4447142.html 1 boost原子变量和线程 #include <boost/thread.hpp& ...

  8. CPU时钟是个什么东西

    CPU时钟说的是频率生成器,可能生成的是方波也可能是其它波. 频率生成器那是什么东西?这教要讲到压电效应了. 压电效应这个词是一个统称,它包括正压电效应和逆压电效应. 传说在很久以前有一个科学家发现了 ...

  9. Java-马士兵设计模式学习笔记-工厂模式-单例及多例

    一.单例的作用是用于控制类的生成方式,而不让外部类任意new对象 1.Car.java import java.util.ArrayList; import java.util.List; publi ...

  10. 一个ButtonDemo序(遇到的问题,以及在大牛的帮助下,如何解决的。)

    问题1: public ButtonDemo(){ //ImageIcon leftButtonIcon=new ImageIcon("images/a.png"); ImageI ...