网络流之最大流算法(EK算法和Dinc算法)
最大流
网络流的定义:
在一个网络(有流量)中有两个特殊的点,一个是网络的源点(s),流量只出不进,一个是网络的汇点(t),流量只进不出。
最大流:就是求s-->t的最大流量
假设 u,v 两个点,连接这两个点的边为e(u,v);
对于每一条边都有一个实际流量f(u,v),还有一个容量c(u,v),就是这条边上可以通过的最大流量。
当一条边的容量c(u,v)=0,证明这条边是不存在的,
作为一个合格的网络流,必须满足三个条件:
1>每条边的实际流量小于等于容量 f(u,v)<=c(u,v);
2>f(u,v)=-f(v,u);
3>对于不是源点和汇点的点,流入的流量等于流出的流量
如何来求一个网络的最大流:
如图是一个网路流,很明显看出答案是4。
我们要求s-t的流量,我们可以选择这样来求解,我们先从s点出发,找到一条s-t的路径,记录这条路径上那个最小的实际流量,
算法就是我们要找到很多条这样的路径,但这些路径都应该是不同的,所有我们只需要把这多条路径的的最小流量相加 得到就是最大流、
这个寻找s-t的路径也叫做增广路算法。
其实这里困难的就是如何保证这些路径是不会相同的 这是涉及到一个概念 就是残留网络
残留网络就是每次利用增广路算法找到这条路径的最小实际流量 minn,我们在原网络中把这条边的容量都减去minn,所以必定这条路径中一定会有流量为0。
所有下次增广的话,就一定不会走原路 因为这条路径中有边的流量有0,走是没有意义的。一直到不能增广为止,得到的和就是最大流
如上图 我们可以很好的求出最大流
先增广 找到 s-1-t 这条路 minn=2 所以他的残留网络图变为

继续增广,得的残留网络为:

这样很容易求到最大流,但这种其实是错误的,比如我们换一个图

假设我们先增广 s-1-2-t, 其实我们就只不在增广了,结果等于2,其实这答案是4,这里就要体现反向边的作用了
我们最开始设的 反向边的值都是0的,就是我们在每次增广后的残流网络不仅边的流量要减去minn,反向弧的流量应该加上minn
比如 ,你先增广s-1-2-t 残留网络为

这样我们依然可以继续增广,最终可以得到答案为4
就是给了一个反悔的机会,就是比如我有这条的反向边我依然不能增广,哪这就是无所谓的额,
当我们第二次的增广路走2-1这条反向边的时候,就相当于把1-2这条正向边已经是用了的流量给”退”了回去,不走1-2这条路,而改走从1点出发的其他的路也就是1-t。 (有人问如果这里没有1-t怎么办,这时假如没有1-t这条路的话,最终这条增广路也不会存在,因为他根本不能走到汇点)
同时本来在2-t上的流量由s-2-t这条路来”接管”.
这是就网络流最大流中最简单的一个EK算法了(全名不记得了)
下面给出一道入门题 hdu 3549
Flow Problem
Time Limit: 5000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 16416 Accepted Submission(s): 7747
flow is a well-known difficult problem for ACMers. Given a graph, your
task is to find out the maximum flow for the weighted directed graph.
For
each test case, the first line contains two integers N and M, denoting
the number of vertexes and edges in the graph. (2 <= N <= 15, 0
<= M <= 1000)
Next M lines, each line contains three integers
X, Y and C, there is an edge from X to Y and the capacity of it is C. (1
<= X, Y <= N, 1 <= C <= 1000)
Case 2: 2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<cmath>
typedef long long ll;
typedef unsigned long long LL;
using namespace std;
const double PI=acos(-1.0);
const double eps=0.0000000001;
const int INF=1e9;
const int N=+;
int mp[N][N];
int vis[N];
int pre[N];
int m,n;
int BFS(int s,int t){
queue<int>q;
memset(pre,-,sizeof(pre));
memset(vis,,sizeof(vis));
pre[s]=;
vis[s]=;
q.push(s);
while(!q.empty()){
int p=q.front();
q.pop();
for(int i=;i<=m;i++){
if(mp[p][i]>&&vis[i]==){
pre[i]=p;
vis[i]=;
if(i==t)return ;
q.push(i);
}
}
}
return false;
}
int EK(int s,int t){
int flow=;
//cout<<BFS(s,t)<<endl;
while(BFS(s,t)){
//BFS(s,t);
int dis=INF;
for(int i=t;i!=s;i=pre[i])
dis=min(mp[pre[i]][i],dis);
for(int i=t;i!=s;i=pre[i]){
mp[pre[i]][i]=mp[pre[i]][i]-dis;
mp[i][pre[i]]=mp[i][pre[i]]+dis;
}
flow=flow+dis;
}
return flow;
}
int main(){
int Case;
cin>>Case;
int tt=;
while(Case--){
scanf("%d%d",&m,&n);
memset(mp,,sizeof(mp));
for(int i=;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
mp[u][v]=w+mp[u][v];
// mp[v][u]=0;
}
int ans=EK(,m);
cout<<"Case "<<tt++<<":"<<" ";
cout<<ans<<endl;
} }
由于EK算法容易超时 所有这个在比赛中不怎么用 所以我们就需要复杂度低的 接下来我们就介绍Dinc算法
其实Dinc算法和EK是很相似的,Dinc中有一个概念 叫做层次图,就是这个使其复杂度低了很多的,主要就是一个多路增广,
就是在BFS一遍的过程中都达到多路增广,而减少复杂度
如下图

Dinc的写法 也是一个板子
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<string.h>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<cmath>
typedef long long ll;
typedef unsigned long long LL;
using namespace std;
const double PI=acos(-1.0);
const double eps=0.0000000001;
const int INF=1e9;
const int N=+;
int head[N];
int dis[N];
int tot;
int n,m;
struct node{
int to,next,flow;
}edge[N<<];
void init(){
memset(head,-,sizeof(head));
tot=;
}
void add(int u,int v,int c){
edge[tot].to=v;
edge[tot].flow=c;
edge[tot].next=head[u];
head[u]=tot++;
}
int BFS(int s,int t){
queue<int>q;
memset(dis,-,sizeof(dis));
q.push(s);
dis[s]=;
while(!q.empty()){
int x=q.front();
q.pop();
if(x==t)return ;
for(int i=head[x];i!=-;i=edge[i].next){
int v=edge[i].to;
if(edge[i].flow&&dis[v]==-){
dis[v]=dis[x]+;
q.push(v);
}
}
}
if(dis[t]==-)return ;
else
return ;
}
int DFS(int s,int flow){
if(s==m)return flow;
int ans=;
for(int i=head[s];i!=-;i=edge[i].next){
int v=edge[i].to;
if(edge[i].flow&&dis[v]==dis[s]+){
int f=DFS(v,min(flow-ans,edge[i].flow));
edge[i].flow-=f;
edge[i^].flow+=f;
ans+=f;
if(ans==flow)return ans;
}
}
return ans;
}
int Dinc(int s,int t){
int flow=;
while(BFS(s,t)){
flow+=DFS(s,INF);
}
return flow;
}
int main(){
int Case;
scanf("%d",&Case);
int tt=;
while(Case--){
init();
scanf("%d%d",&m,&n);
for(int i=;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,);
// mp[v][u]=0;
}
int ans=Dinc(,m);
cout<<"Case "<<tt++<<":"<<" ";
cout<<ans<<endl;
} }
网络流之最大流算法(EK算法和Dinc算法)的更多相关文章
- 单源最短路径算法——Bellman-ford算法和Dijkstra算法
BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V ...
- TCP_NODELAY和TCP_CORK nagle算法和cork算法
TCP_NODELAY 默认情况下,发送数据採用Nagle 算法.这样尽管提高了网络吞吐量,可是实时性却减少了,在一些交互性非常强的应用程序来说是不同意的.使用TCP_NODELAY选项能够禁止Nag ...
- 使用Apriori算法和FP-growth算法进行关联分析
系列文章:<机器学习实战>学习笔记 最近看了<机器学习实战>中的第11章(使用Apriori算法进行关联分析)和第12章(使用FP-growth算法来高效发现频繁项集).正如章 ...
- 最小生成树---Prim算法和Kruskal算法
Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gra ...
- mahout中kmeans算法和Canopy算法实现原理
本文讲一下mahout中kmeans算法和Canopy算法实现原理. 一. Kmeans是一个很经典的聚类算法,我想大家都非常熟悉.虽然算法较为简单,在实际应用中却可以有不错的效果:其算法原理也决定了 ...
- 转载:最小生成树-Prim算法和Kruskal算法
本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算 ...
- 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论
一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...
- 用Spark学习FP Tree算法和PrefixSpan算法
在FP Tree算法原理总结和PrefixSpan算法原理总结中,我们对FP Tree和PrefixSpan这两种关联算法的原理做了总结,这里就从实践的角度介绍如何使用这两个算法.由于scikit-l ...
- 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)
字符串匹配是字符串的一种基本操作:给定一个长度为 M 的文本和一个长度为 N 的模式串,在文本中找到一个和该模式相符的子字符串,并返回该字字符串在文本中的位置. KMP 算法,全称是 Knuth-Mo ...
随机推荐
- java1.8学习-什么样的匿名内部类能被lambda语法代替?
java1.8学习-什么样的匿名内部类能被lambda语法代替? java1.8好多新的特性真的很有意思,特别是Lambda.在学习的时候发现并不是所有的匿名内部类都可以用Lambda代替. lamb ...
- mybatis中修改了数据,控制台显示成功,数据库没有修改
在mybatis中遇到了修改数据时,控制台显示修改成功,但是去数据库查看并没有修改,这是因为mybatis不时自动提交事务的,所以是不会修改数据库的数据,这是我们加上一句 sqlSession.com ...
- 标量子查询中有ROWNUM=1怎么改?
碰到标量子查询中有ROWNUM=1怎么改? select to_date(o.postdate,'yyyymmdd'), (select cur.c_code from cur_tbl cur whe ...
- solr-5.3.1配置(win7 x64)
下载solr,下载地址http://www.eu.apache.org/dist/lucene/solr/5.3.1/solr-5.3.1.zip 解压到某个目录下,这里是解压到了d盘目录下,路径:D ...
- Python之“Hello World”
Python之“Hello World” 了解Python: 编译型和解释型 编译:把明文代码执行前,先转换成二进制,在执行.这个过程叫编译 解释器:将明文代码转成二进制的 Linux中,gcc编译, ...
- vue中的表单验证
http://www.cnblogs.com/luoxuemei/p/9295506.html /*是否合法IP地址*/ export function validateIP(rule, value, ...
- 82-Ichimoku Kinko Hyo 一目平衡表.(2015.7.3)
Ichimoku Kinko Hyo 一目平衡表 计算: 一目平衡图由五组参数合成,与现在常用的移动平均线吻合.参数基于各个长短周期的高低点,提供一明确简单的走势图.五个参数如下: 1.短轴快线 短轴 ...
- echarts的简单应用之(一)柱形图
前段时间做项目需要绘制一些图表来展示信息,从网上资料来看用的比较多的是百度开源的echarts.echarts的官网上有API以及demo.上面的图形都是非常炫的,不过都是静态 数据,我们做项目时不可 ...
- git-github 提示Permission denied (publickey) (windows)
这种绝大多数情况是由于公钥设置的问题. 1.生成公钥 参考地址:https://help.github.com/articles/generating-a-new-ssh-key-and-adding ...
- hihoCoder#1036 Trie图
原题地址 看了这篇博文,总算是把Trie图弄明白了 Runtime Error了无数次,一直不知道为什么,于是写了个脚本生成了一组大数据,发现果然段错误了. 调试了一下午,总算闹明白了,为什么呢? 1 ...