网络流Dinic(本篇介绍最大流)
前言
看到网上Dinic和ISAP的比较,多数人认为ISAP更快,不容易爆栈。当然,也有少数人认为,在多数情况下,Dinic比较稳定。我认为Dinic的思路比ISAP更简明,所以选择了Dinic算法
UPD20190626:突然发现这篇博客阅读量1000多了,把我吓得不轻,感谢各位网友的支持。
介绍
Dinic算法本身,自然是解决最大流(普通最大流,最大流最小割)的算法。通过处理,也可以解决二分图的最大匹配(下文介绍),最大权闭合图。
算法介绍:介绍Dinic之前,我们先介绍一下最大流。在最大流的题目中,图被称为"网络",每条边的边权被称作"流量",有一个起点(源点)和一个终点(汇点)。我们要求的最大流,可以这样形象地理解:源点有一个水库,里面有无限吨水(QWQ),汇点也有一个水 库,希望得到最多的水。我们假设每个河道一天只能输水n吨(及网络流中的流量),求解汇点最多能的到几吨水。再给一个正式的定义:最大流是指网络中满足弧流量限制条件和平衡条件且具有最大流量的可行流
下面我们正式介绍Dinic:
首先引出网络流算法中的链,给个正式定义:链是网络中的一个顶点序列,这个序列中前后两个顶点有弧相连(其实我认为这个定义无关紧要,所以重点看下面弧的定义)。
弧 :弧分为两种,第一种是前向弧是指方向和链一致的弧(简单的说就是输入的边)---前向弧,第二种弧是指方向和链不一致的弧(简单的说就是输入的边反一反)---后向弧。
好了接下来要引出一个网络流算法的重要概念
增广路
给个正式的定义:
1、增广路是一条链
2、链上的前向弧都是非饱和弧
链上的后向弧都是非零弧
3、链是由源点到汇点的
总结一下:额...这听起来好像啥都没说(滑稽)
谈谈我的理解:
增广路是一个边集,是一条从源点到汇点的路径
增广路有一个权值表示该增广路的最大流量,而最大流量的大小是边集中流量最小的边的流量。
剩余网络
由反向弧组成的网络,关于反向弧的权的问题,后文会介绍。
说了一大堆,下面正式介绍Dinic算法
Dinic算法的大致步骤
1、建立网络(包括正向弧和反向弧(初始边权为0)),将总流量置为0
2、构造层次网络(怎么又有新概念 T_T)
简单的说,就是求出每个点u的层次,u的层次是从源点到该点的最短路径(注意:这个最短路是指弧的权都为1的情况下的最短路),若与源点不连通,层次置为-1
一遍BFS轻松解决
3、判断汇点的层次是否为-1
是:再见,算法结束,输出当前的总流量
否:下一步
4、用一次DFS完成所有增广,增广是什么呢?
增广(我的理解):通过DFS找上述的增广路,找到了之后,将每条边的权都减去该增广路中拥有最小流量的边的流量,将每条边的反向边的权增加这个值,同时将总流量加上这个值
DFS直到找不到一条可行的从原点到汇点的路
5、goto 步骤2
细节处理,如何快速找到一条边的反向边:边的编号从0开始,反向边加在正向边之后,反向边即为该点的编号异或1
复杂度:理论上来说,最慢应该是O((n^2)*m),n表点数,m表边数,实际上呢,应该快得不少
代码实例:(参见洛谷P3376)
传送门[>洛谷<] 重要提示:您的等级必须达到蓝色以上,否则后果自负
当前弧优化
在DFS的时候记录当前已经计算到第几条边了,避免重复计算。
那么具体原理是什么呢?
每当我们发现一条新的增广路时,由于算法"贪心"的性质,该增广路上的可用流量其实已经被支配完全(即使放入另一条增广路也毫无意义)。
那么当我们每次到达一个节点时,必定有许多边已经被完全支配,又由于我们深度优先搜索的性质,故可以记录上一次搜索到哪一条边,下一次直接从该边开始DFS。
在下一次构建层次网络时注意将head数组还原
代码
* 使用当前弧优化
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm> using namespace std; const int MAX = (1ll << 31) - 1; int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
} struct Edge{
int to;
int dis;
int next;
} edges[210000]; int cur[10010], head[10010], edge_num = -1;
int n, m, s, t; void addEdge2(int from, int to, int dis){
edges[++edge_num].to = to;
edges[edge_num].dis = dis;
edges[edge_num].next = head[from];
head[from] = edge_num;
} void addEdge(int from, int to, int dis){
addEdge2(from, to, dis), addEdge2(to, from, 0);
} int d[10010]; int DFS(int u, int flow){
if (u == t) return flow;
int _flow = 0, __flow;
for (int& c_e = cur[u]; c_e != -1; c_e = edges[c_e].next){
int v = edges[c_e].to;
if (d[v] == d[u] + 1 && edges[c_e].dis > 0){
__flow = DFS(v, min(flow, edges[c_e].dis));
flow -= __flow;
edges[c_e].dis -= __flow;
_flow += __flow;
edges[c_e^1].dis += __flow;
if (!flow)
break;
}
}
if (!_flow) d[u] = -1;
return _flow;
} bool BFS(){
memset(d, -1, sizeof(d));
queue<int> que; que.push(s);
d[s] = 0; int u, _new;
while (!que.empty()){
u = que.front(), que.pop();
for (int c_e = head[u]; c_e != -1; c_e = edges[c_e].next){
_new = edges[c_e].to;
if (d[_new] == -1 && edges[c_e].dis > 0){
d[_new] = d[u] + 1;
que.push(_new);
}
}
}
return (d[t] != -1);
} void dinic(){
int max_flow = 0;
while (BFS()){
for (int i = 1; i <= n; ++i) cur[i] = head[i];
max_flow += DFS(s, MAX);
}
printf("%d", max_flow);
} int main(){
n = read(), m = read(), s = read(), t = read();
memset(head, -1, sizeof(head));
for (int i = 0; i < m; i++){
int u = read(), v = read(), w = read();
addEdge(u, v, w);
}
dinic();
return 0;
}
算法主要应用场景
1、裸的最大流
2、二分图的最大匹配:建一个点S,连到二分图的集合A中;建一个点T,连到二分图的集合B中。再将所有的集合A中的点与集合B中的点相连。全部边权设为1,跑一遍最大流,结果即为二分图的最大匹配
3、最小割(定义自行百度):在单源单汇流量图中,最大流等于最小割
4、求最大权闭合图(定义自行百度):最大权值=正点权之和-最小割
主要问题:
为什么要建立反向边?
Answer:总结多篇博客,认为建立反向边旨在增加重新调整流的机会,即保障解是最优的(还是没有理解?可以自行百度:D)。
版权申明:未经博主允许禁止转载
网络流Dinic(本篇介绍最大流)的更多相关文章
- 再写一篇tps限流
再写一篇tps限流 各种限流算法的称呼 网上有很多文章介绍限流算法,但是对于这些算法的称呼与描述也是有点难以理解.不管那么多了.我先按我理解的维度梳理一下. 主要维度是:是正向计数还是反向计数.是定点 ...
- 国内首篇介绍JanOS物联网操作系统的文章 - 如何把你的手机主板打造成物联网平台
天地会珠海分舵注:如无意外,您现在正在看的将是国内首篇且是唯一一篇介绍炙手可热的物联网的操作系统JanOS的文章!不信你去百度!希望大家能喜欢.但本文只是引言,更多信息请还是访问JanOS的官网:ht ...
- POJ 1273 Drainage Ditches (网络流Dinic模板)
Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover ...
- Java并发包下锁学习第一篇:介绍及学习安排
Java并发包下锁学习第一篇:介绍及学习安排 在Java并发编程中,实现锁的方式有两种,分别是:可以使用同步锁(synchronized关键字的锁),还有lock接口下的锁.从今天起,凯哥将带领大家一 ...
- spring cloud系列教程第一篇-介绍
spring cloud系列教程第一篇-介绍 前言: 现在Java招聘中最常见的是会微服务开发,微服务已经在国内火了几年了,而且也成了趋势了.那么,微服务只是指spring boot吗?当然不是了,微 ...
- 老猿学5G扫盲贴:推荐三篇介绍HTTP2协议相关的文章
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 5G中的服务化接口调用都是基于HTTP2协议的,老 ...
- 网络流小记(EK&dinic&当前弧优化&费用流)
欢 迎 来 到 网 络 瘤 的 世 界 什么是网络流? 现在我们有一座水库,周围有n个村庄,每个村庄都需要水,所以会修水管(每个水管都有一定的容量,流过的水量不能超过容量).最终水一定会流向唯一一个废 ...
- POJ 2987 Firing 最大流 网络流 dinic 模板
https://www.cnblogs.com/137shoebills/p/9100790.html http://poj.org/problem?id=2987 之前写过这道题,码一个dinic的 ...
- POJ 3281(Dining-网络流拆点)[Template:网络流dinic]
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmlrZTBnb29k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
随机推荐
- 手把手教你实现Android RecyclerView上拉加载功能
摘要 一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码, ...
- NetSec2019 20165327 Exp7 网络欺诈防范
NetSec2019 Exp7 网络欺诈防范 一.本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体实践有 (1)简单应用SET工具建立冒名网站 (1分) (2)ette ...
- CCPC-Wannafly Winter Camp Day1部分题目解析
Preface 最近恰好不知道做什么题,所以就按老叶要求做上面的比赛. 稍微看了下感觉难度适中,大部分题目偏向联赛难度,当然也有些题目打到了省选题的感觉(基本都是Div1的题) 这里就简单拿一些我做得 ...
- 关于IOC容器的一些个人理解
一丶前言 下面是本人对于IOC容器的一些个人理解,希望能帮到初学者认识IOC,如有理解得不对的地方欢迎指正,也让我学学. 二丶IOC是什么,它是干嘛的? IOC只是一种编程思想,不局限于任何一种语言, ...
- JS操作数组-2
1. 找出数组 arr 中重复出现过的元素 function duplicates(arr) { var result = []; var count = []; for (var i=0;i< ...
- [第二届构建之法论坛] 预培训文档(C++版)
本博客是第二届构建之法论坛暨软件工程培训活动预培训文档中[适用于结对编程部分的C++版本],需要实验者有一部分C++基础. 目录 Part0.背景 Part1.配置环境 Part2.克隆项目 Part ...
- 题解 P1601 【A+B Problem(高精)】
P1601 A+B Problem(高精) 题目描述 高精度加法,x相当于a+b problem,b不用考虑负数. 输入输出格式 输入格式: 分两行输入a,b<=10^500 输出格式: 输出只 ...
- Python——Set集合
一.定义 Set集合用于表示相互之间无需的一种组合对象,包括:并集.交集.补集 二.集合的两种模式 sample = set() 初始化普通集合 sample = frozenset() 初始化不可 ...
- HDU6278 Just h-index
主席树+二分 每次对给定区间从1-区间长度len二分mid,查询区间内第mid大的数是不是大于等于mid.. #include <bits/stdc++.h> #define INF 0x ...
- [LOJ10121] 与众不同
题目类型:\(DP\)+\(RMQ\) 传送门:>Here< 题意:给定一个长度为\(N\)的序列,并给出\(M\)次询问.询问区间\([L,R]\)内的最长完美序列.所谓完美序列就是指连 ...