最大流

网络流的定义:

在一个网络(有流量)中有两个特殊的点,一个是网络的源点(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

Problem Description
Network
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.
Input
The first line of input contains an integer T, denoting the number of test cases.
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)
Output
For each test cases, you should output the maximum flow from source 1 to sink N.
Sample Input
2
3 2
1 2 1
2 3 1
3 3
1 2 1
2 3 1
1 3 1
Sample Output
Case 1:
1
Case 2: 2
Author
HyperHexagon
Source
 #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算法)的更多相关文章

  1. 单源最短路径算法——Bellman-ford算法和Dijkstra算法

     BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V ...

  2. TCP_NODELAY和TCP_CORK nagle算法和cork算法

    TCP_NODELAY 默认情况下,发送数据採用Nagle 算法.这样尽管提高了网络吞吐量,可是实时性却减少了,在一些交互性非常强的应用程序来说是不同意的.使用TCP_NODELAY选项能够禁止Nag ...

  3. 使用Apriori算法和FP-growth算法进行关联分析

    系列文章:<机器学习实战>学习笔记 最近看了<机器学习实战>中的第11章(使用Apriori算法进行关联分析)和第12章(使用FP-growth算法来高效发现频繁项集).正如章 ...

  4. 最小生成树---Prim算法和Kruskal算法

    Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gra ...

  5. mahout中kmeans算法和Canopy算法实现原理

    本文讲一下mahout中kmeans算法和Canopy算法实现原理. 一. Kmeans是一个很经典的聚类算法,我想大家都非常熟悉.虽然算法较为简单,在实际应用中却可以有不错的效果:其算法原理也决定了 ...

  6. 转载:最小生成树-Prim算法和Kruskal算法

    本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算 ...

  7. 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论

    一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...

  8. 用Spark学习FP Tree算法和PrefixSpan算法

    在FP Tree算法原理总结和PrefixSpan算法原理总结中,我们对FP Tree和PrefixSpan这两种关联算法的原理做了总结,这里就从实践的角度介绍如何使用这两个算法.由于scikit-l ...

  9. 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)

    字符串匹配是字符串的一种基本操作:给定一个长度为 M 的文本和一个长度为 N 的模式串,在文本中找到一个和该模式相符的子字符串,并返回该字字符串在文本中的位置. KMP 算法,全称是 Knuth-Mo ...

随机推荐

  1. 创建Tensor

    目录 创建Tensor numpy, list numpy list zeros, ones, fill zeros ones fill random 打乱idx后,a和b的索引不变 constant ...

  2. python的学习之路(三)

    一.set集合#!/usr/bin/env python# *_*coding:utf-8 *_*# Author: harson old_dict = { "#1": {'hos ...

  3. 选项B中:int b[][3]={0,1,2,3}

    选项B中:int b[][3]={0,1,2,3};等价于 int b[][3]={0,1,2,3,0,0};    int b[][3]={0,1,2,3,4};         cout<& ...

  4. [Zabbix] 如何实现邮件报警通知以及免费短信报警通知

    版权声明:本文为博主原创文章,未经博主允许不得转载.  前提条件: (1) zabbix服务器端已经成功安装并且运行. (2) zabbix客户端已经成功建立并且运行. 1 下载并且安装msmtp软件 ...

  5. mysql ab主从复制出错及解决过程

    一.mysql主从服务器报错描述:Slave_IO_Running=NO,Slave_SQL_Running=YES,Last_Errno=0 mysql slave stop ; mysql sla ...

  6. HDU 4945 (dp+组合数学)

    2048 Problem Description Teacher Mai is addicted to game 2048. But finally he finds it's too hard to ...

  7. 【bzoj2527】[Poi2011]Meteors(树状数组(单点查询,区间修改)+整体二分)

    [bzoj2527][Poi2011]Meteors Description Byteotian Interstellar Union (BIU) has recently discovered a ...

  8. poj 1752 Advertisement (差分约束)

    题目大意:题目大意:有n个人在一条路上跑步,广告商准备在这条路上设置广告牌,假设这条路上每一个点有一个广告牌 现在已知这n个人从Ai开始跑,到Bi结束,那么他可以看到max(Ai,Bi)-min(Ai ...

  9. [K/3Cloud] 树形单据体的应用说明

    1.BOSIDE制作单据,支持动态表单,单据,基础资料,报表等域模型. 2.添加列,和原来单据体一样. 3.设置主键列名,父级主键字段名,行类型字段名,节点图片字段名(没有可以为空)   4.运行时展 ...

  10. Layui动画、按钮、表单

    Layui动画.按钮.表单 在实用价值的前提之下,我们并没有内置过多花俏的动画.而他们同样在 layui 的许多交互元素中,发挥着重要的作用.layui 的动画全部采用 CSS3,因此不支持ie8和部 ...