网络流(四)dinic算法
传送门:
网络流(一)基础知识篇
网络流(二)最大流的增广路算法
网络流(三)最大流最小割定理
网络流(四)dinic算法
网络流(五)有上下限的最大流
网络流(六)最小费用最大流问题
转自:https://www.cnblogs.com/SYCstudio/p/7260613.html
朴素算法的低效之处
虽然说我们已经知道了增广路的实现,但是单纯地这样选择可能会陷入不好的境地,比如说这个经典的例子:

我们一眼可以看出最大流是999(s->v->t)+999(s->u->t),但如果程序采取了不恰当的增广策略:s->v->u->t

我们发现中间会加一条u->v的边

而下一次增广时:

若选择了s->u->v->t

然后就变成

这是个非常低效的过程,并且当图中的999变成更大的数时,这个劣势还会更加明显。
怎么办呢?
引入Dinic算法
以下转自:https://www.cnblogs.com/SYCstudio/p/7260613.html
预备知识:
残留网络:设有容量网络G(V,E)及其上的网络流f,G关于f的残留网络即为G(V',E'),其中G’的顶点集V'和G的顶点集V相同,即V'=V,对于G中任何一条弧<u,v>,如果f(u,v)<c(u,v),那么在G'中有一条弧<u,v>∈E',其容量为c'(u,v)=c(u,v)-f(u,v),如果f(u,v)>0,则在G'中有一条弧<v,u>∈E',其容量为c’(v,u)=f(u,v).
从残留网络的定义来看,原容量网络中的每条弧在残留网络中都化为一条或者两条弧。在残留网络中,从源点到汇点的任意一条简单路径都对应一条增光路,路径上每条弧容量的最小值即为能够一次增广的最大流量。

顶点的层次:在残留网络中,把从源点到顶点u的最短路径长度,称为顶点u的层次。源点 Vs的层次为0.例如下图就是一个分层的过程。

注意:
(1)对残留网路进行分层后,弧可能有3种可能的情况。
1、从第i层顶点指向第i+1层顶点。
2、从第i层顶点指向第i层顶点。
3、从第i层顶点指向第j层顶点(j < i)。
(2)不存在从第i层顶点指向第i+k层顶点的弧(k>=2)。
(3)并非所有的网络都能分层。

2、最短路增广路径的算法思想
最短增广路的算法思想是:每次在层次网络中找一条含弧数最少的增广路进行增广。最短增广路算法的具体步骤如下:
(1)初始化容量网络和网络流。
(2)构造残留网络和层次网络,若汇点不在层次网络中,则算法结束。
(3)在层次网络中不断用BFS增广,直到层次网络中没有增广路为止;每次增广完毕,在层次网络中要去掉因改进流量而导致饱和的弧。
(4)转步骤(2)。
在最短增广路算法中,第(2)、(3)步被循环执行,将执行(2)、(3)步的一次循环称为一个阶段。在每个阶段中,首先根据残留网络建立层次网络,然后不断用BFS在层次网络中增广,直到出现阻塞流。注意每次增广后,在层次网络中要去掉因改进流量而饱和的弧。该阶段的增广完毕后,进入下一阶段。这样的不断重复,直到汇点不在层次网络中为止。汇点不在层次网络中意味着在残留网络中不存在一条从源点到汇点的路径,即没有增广路。
在程序实现的时候,并不需要真正“构造”层次网络,只需要对每个顶点标记层次,增广的时候,判断边是否满足level(v) = level(u)+1这一约束条件即可。
3、最短增广路算法复杂度分析
最短增广路的复杂度包括建立层次网络和寻找增广路两部分。
在最短增广路中,最多建立n个层次网络,每个层次网络用BFS一次遍历即可得到。一次BFS的复杂度为O(m),所以建层次图的总复杂度为O(n*m)。
每增广一次,层次网络中必定有一条边会被删除。层次网络中最多有m条边,所以认为最多可以增广m次。在最短增广路算法中,用BFS来增广,一次增广的复杂度为O(n+m),其中O(m)为BFS的花费,O(n)为修改流量的花费。所以在每一阶段寻找增广路的复杂度为O(m*(m+n)) = O(m*m)。因此n个阶段寻找增广路的算法总复杂度为O(n*m*m)。
两者之和为O(n*m*m)。
以上介绍最短增广路算法只是为下面介绍Dinic算法而提供给大家一个铺垫,有了以上的基础,接下来我们来介绍Dinic算法,Dinic其实是最短增广路算法的优化版。

连续最短增广路算法----Dinic算法
1、Dinic算法思路
Dinic算法的思想也是分阶段地在层次网络中增广。它与最短增广路算法不同之处是:最短增广路每个阶段执行完一次BFS增广后,要重新启动BFS从源点Vs开始寻找另一条增广路;而在Dinic算法中,只需一次DFS过程就可以实现多次增广,这是Dinic算法的巧妙之处。Dinic算法具体步骤如下:
(1)初始化容量网络和网络流。
(2)构造残留网络和层次网络,若汇点不再层次网络中,则算法结束。
(3)在层次网络中用一次DFS过程进行增广,DFS执行完毕,该阶段的增广也执行完毕。
(4)转步骤(2)。
在Dinic的算法步骤中,只有第(3)步与最短增广路相同。在下面实例中,将会发现DFS过程将会使算法的效率有非常大的提高。
Dinic算法复杂度分析
与最短增广路算法一样,Dinic算法最多被分为n个阶段,每个阶段包括建层次网络和寻找增广路两部分,其中建立层次网络的复杂度仍是O(n*m)。
现在来分析DFS过程的总复杂度。在每一阶段,将DFS分成两部分分析。
(1)修改增广路的流量并后退的花费。在每一阶段,最多增广m次,每次修改流量的费用为O(n)。而一次增广后在增广路中后退的费用也为O(n)。所以在每一阶段中,修改增广路以及后退的复杂度为O(m*n)。
(2)DFS遍历时的前进与后退。在DFS遍历时,如果当前路径的最后一个顶点能够继续扩展,则一定是沿着第i层的顶点指向第i+1层顶点的边向汇点前进了一步。因为增广路经长度最长为n,所以最坏的情况下前进n步就会遇到汇点。在前进的过程中,可能会遇到没有边能够沿着继续前进的情况,这时将路径中的最后一个点在层次图中删除。
注意到每后退一次必定会删除一个顶点,所以后退的次数最多为n次。在每一阶段中,后退的复杂度为O(n)。
假设在最坏的情况下,所有的点最后均被退了回来,一共共后退了n次,这也就意味着,有n次的前进被“无情”地退了回来,这n次前进操作都没有起到“寻找增广路”的作用。除去这n次前进和n次后退,其余的前进都对最后找到增广路做了贡献。增广路最多找到m次。每次最多前进n个点。所以所有前进操作最多为n+m*n次,复杂度为O(n*m)。
于是得到,在每一阶段中,DFS遍历时前进与后退的花费为O(m*n)。
综合以上两点,一次DFS的复杂度为O(n*m)。因此,Dinic算法的总复杂度即O(m*n*n)。

struct edge
{
int u, v, c, f;
edge(int u, int v, int c, int f):u(u), v(v), c(c), f(f){}
};
vector<edge>e;
vector<int>G[maxn];
int level[maxn];//BFS分层,表示每个点的层数
int iter[maxn];//当前弧优化
int m;
void init(int n)
{
for(int i = ; i <= n; i++)G[i].clear();
e.clear();
}
void addedge(int u, int v, int c)
{
e.push_back(edge(u, v, c, ));
e.push_back(edge(v, u, , ));
m = e.size();
G[u].push_back(m - );
G[v].push_back(m - );
}
void BFS(int s)//预处理出level数组
//直接BFS到每个点
{
memset(level, -, sizeof(level));
queue<int>q;
level[s] = ;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int v = ; v < G[u].size(); v++)
{
edge& now = e[G[u][v]];
if(now.c > now.f && level[now.v] < )
{
level[now.v] = level[u] + ;
q.push(now.v);
}
}
}
}
int dfs(int u, int t, int f)//DFS寻找增广路
{
if(u == t)return f;//已经到达源点,返回流量f
for(int &v = iter[u]; v < G[u].size(); v++)
//这里用iter数组表示每个点目前的弧,这是为了防止在一次寻找增广路的时候,对一些边多次遍历
//在每次找增广路的时候,数组要清空
{
edge &now = e[G[u][v]];
if(now.c - now.f > && level[u] < level[now.v])
//now.c - now.f > 0表示这条路还未满
//level[u] < level[now.v]表示这条路是最短路,一定到达下一层,这就是Dinic算法的思想
{
int d = dfs(now.v, t, min(f, now.c - now.f));
if(d > )
{
now.f += d;//正向边流量加d
e[G[u][v] ^ ].f -= d;
//反向边减d,此处在存储边的时候两条反向边可以通过^操作直接找到
return d;
}
}
}
return ;
}
int Maxflow(int s, int t)
{
int flow = ;
for(;;)
{
BFS(s);
if(level[t] < )return flow;//残余网络中到达不了t,增广路不存在
memset(iter, , sizeof(iter));//清空当前弧数组
int f;//记录增广路的可增加的流量
while((f = dfs(s, t, INF)) > )
{
flow += f;
}
}
return flow;
}
网络流(四)dinic算法的更多相关文章
- [知识点]网络流之Dinic算法
// 此博文为迁移而来,写于2015年2月6日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vrg4.html ...
- [无效]网络流之Dinic算法
// 此博文为迁移而来,写于2015年2月6日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vrg4.html UPDA ...
- 网络流 之 dinic算法
我觉得这个dinic的算法和之前的增广路法差不多 .使用BFS对残余网络进行分层,在分层时,只要进行到汇点的层次数被算出即可停止, 因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点 ...
- 网络流之Dinic算法
初学网络流.存一下Dinic板子. 复杂度O(n^2*m) UVA - 1515 Pool construction 把每个草地与 S 相连,花费为dig,每个洞与 T 相连,花费为 然后对于每个两个 ...
- 网络流 之 dinic 算法
网络流指的是:网络流(network-flows)是一种类比水流的解决问题方法.(类似于水管群,有一个源点(水无限多),和一个汇点,最大流就代表这个点水管群(边集)每秒最大能送道汇点的水量) 这个怎么 ...
- Secret Milking Machine POJ - 2455 网络流(Dinic算法---广搜判断+深搜增广)+时间优化+二分
题意: 第一行输入N M C ,表示从1到N有M条无向边,现在要从1走到N 走C次完全不同的路径,求最长边的最小值.下面M行是从a点到b点的距离. 建图: 题上说从两点之间可以有多条边,问的是从1~N ...
- 网络流(dinic算法)
洛谷p3376 https://www.luogu.com.cn/problem/P3376 #include <iostream> #include <cstdio> #in ...
- 网络流最大流——dinic算法
前言 网络流问题是一个很深奥的问题,对应也有许多很优秀的算法.但是本文只会讲述dinic算法 最近写了好多网络流的题目,想想看还是写一篇来总结一下网络流和dinic算法以免以后自己忘了... 网络流问 ...
- hiho一下 第115周:网络流一•Ford-Fulkerson算法 (Edmond-Karp,Dinic,SAP)
来看一道最大流模板水题,借这道题来学习一下最大流的几个算法. 分别用Edmond-Karp,Dinic ,SAP来实现最大流算法. 从运行结过来看明显SAP+当前弧优化+gap优化速度最快. hi ...
随机推荐
- Java 容器源码分析之ArrayBlockingQueue和LinkedBlockingQueue
Java中的阻塞队列接口BlockingQueue继承自Queue接口. BlockingQueue接口提供了3个添加元素方法. add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会 ...
- 团队作业4——beta阶段冲刺
Deadline: 2018-12-16 22:00PM,以博客提交至班级博客时间为准 提交: (a) 项目课堂演示: (b) 1篇冲刺准备+5篇冲刺随笔,: (c) 1篇用户使用用报告: (d) 1 ...
- 偏流角(Draft Angle)在等距螺旋中的作用
劳动改变人,思维改变世界.我们可以接着聊螺旋线了. 在飞行程序设计中,偏流角(Draft Angle简写为DA)通常指得是受侧风影响航向偏移的最大角度.用速度向量来表示时,是图1中的三角形关系: 图1 ...
- C# 元数据描述
元数据概述:元数据是一种二进制信息,用以对存储在公共语言运行库可移植可执行文件 (PE) 文件或存储在内存中的程序进行描述.将您的代码编译为 PE 文件时,便会将元数据插入到该文件的一部分中,而将代码 ...
- c++中虚析构函数
当指向基类的指针指向新建立的派生类对象而且基类和派生类都调用new向堆申请空间时,必须将基类的析构函数声明为虚函数,从而派生类的析构函数也为虚函数,这样才能在程序结束时自动调用它,从而将派生类对象申请 ...
- MyBatis——MyEclipse中使用mybatis-generator
mybatis-generator可以根据数据库的表来生成POJO类.mapper.xml和DAO接口,用这个插件会大大地提高开发的效率.网上虽然有一些使用这个插件的教程,但我单个试了并不能成功,会出 ...
- asp.net 获取网站根地址
public static string GetSiteRoot() { string port = System.Web.HttpContext.Current.Request.ServerVari ...
- C#生成ZIP压缩包
生成ZIP压缩包C#代码如下: using System; using System.Collections.Generic; using System.Text; using ICSharpCode ...
- MVC之Ajax异步操作
在页面的局部通过Html.Action加载一块分布页,我们可以通过Ajax异步去更换或更新这块分布页 通过一个下拉框值发生变化的时候,我们会去从后台重新访问这个action获取一个新的View,然后替 ...
- Java框架之Struts2(三)
一.几种国际化输出方式 国际化:国际化的本质,就是查找,替换.程序界面上输出的是国际化消息的 key 所对应的值.Struts2 中的国际化机制,会根据 key 到资源文件中找对应的文本来替换上面的k ...