初涉网络流[EK&dinic]
主要还是板子
Edmonds-Karp
从S开始bfs,直到找到一条到达T的路径后将该路径增广,并重复这一过程。
在处理过程中,为了应对“找到的一条路径把其他路径堵塞”的情况,采用了建反向弧的方式来实现“反悔”过程。
这种“反悔”的想法和技巧值得借鉴。
int maxFlow()
{
int ret = ;
for (;;)
{
memset(f, , sizeof f);
memset(bck, , sizeof bck);
std::queue<int> q;
f[S] = INF, q.push(S);
for (int tmp; q.size(); )
{
tmp = q.front(), q.pop();
for (int i=head[tmp]; i!=-; i=nxt[i])
{
int v = edges[i].v;
if (!f[v]&&edges[i].f < edges[i].c){
f[v] = std::min(f[tmp], edges[i].c-edges[i].f);
bck[v] = i, q.push(v);
}
}
if (f[T]) break;
}
if (!f[T]) break;
for (int i=T; i!=S; i=edges[bck[i]].u)
{
edges[bck[i]].f += f[T];
edges[bck[i]^].f -= f[T];
}
ret += f[T];
}
return ret;
}
Dinic
EK的效率是$O(nm^2)$的,它把很多时间浪费在了重复的搜索上面。
dinic有如下两个重要的定义:
- 层次$\text{level(x)}$:表示点$x$在层次图中与源点$S$的距离。
- 层次图:在原来的残量网络当中,只保留所有可被增广的边以及与之相连的点。
bfs建出来的层次图对于接下去的dfs增广具有一种“指导”作用。使用了反向弧技巧,意味着不管用什么方法,只需要找到一条增广路就行。在这种情况下,我们来考虑dfs增广的优劣之处:一方面它一旦找到一条增广路就能快速退出,比bfs的逐级外扩更高效;另一方面纯粹的dfs受搜索顺序的影响很大,因为(可以像卡SPFA以及某些图论算法一样)挂一些诱导节点附带数量巨大的边,就能置dfs于死地。但是这里dfs依靠建出来的层次图,每次只向距离+1的点搜索。这意味着我们避免了对同一个节点的重复搜索,或是偏离T方向浪费时间。
bool buildLevel()
{
memset(lv, , sizeof lv);
std::queue<int> q;
q.push(S), lv[S] = ;
for (int i=; i<=T; i++) cur[i] = head[i]; //tip1
for (int tmp; q.size(); )
{
tmp = q.front(), q.pop();
for (int i=head[tmp]; i!=-; i=nxt[i])
{
int v = edges[i].v;
if (!lv[v]&&edges[i].f < edges[i].c){
lv[v] = lv[tmp]+, q.push(v);
if (v==T) return true; //tip2
}
}
}
return false;
}
int fndPath(int x, int lim) //此处已更新,详情见下
{
if (x==T) return lim;
for (int &i=cur[x]; i!=-; i=nxt[i]) //tip1
{
int v = edges[i].v, val;
if (lv[x]+==lv[v]&&edges[i].f < edges[i].c){
if ((val = fndPath(v, std::min(lim, edges[i].c-edges[i].f)))){
edges[i].f += val, edges[i^].f -= val;
return val;
}else lv[v] = -; //tip3
}
}
cur[x] = head[x];
return ;
}
int dinic()
{
int ret = , val;
while (buildLevel())
while ((val = fndPath(S, INF))) ret += val;
return ret;
}
dinic有三个常见优化:
tip1当前弧优化:这个优化是针对边的,有些网络流的边数巨大。这个优化是为了确保在同一层次图的多次增广当中,可以实现“从上一次成功增广停下的地方再次开始”这一个功能。
tip2层次图优化:每次建层次图只需要达到T即可。
tip3堵塞点优化:姑且这么叫吧……在同一层次图下,一个点若未被增广则再也不会被增广了。
个人觉得tip3的效果最明显。tip1是为了少遍历一些边,但是节省的只不过是遍历(因为并不执行操作)的代价;tip2是看脸的优化;tip3应该算是强剪枝。
3.5upd:
今天写最大权闭合子图时候,才发现我学了个假的dinic.
当时是照着menci的 Dinic 学习笔记 学的dinic,然而今天才发现,menci的指针小常数真的是非常人可比拟的……
就拿bzoj1497: [NOI2006]最大获利来说吧:同样的流程结构,我结构体写法用时7.5s;menci的指针版本只需要0.75s(本地不开O2),这比我加满优化(包括改成以下这个写法)都要快得多……
dinic需要多路优化,而非以上dfs提到的每次寻找到一条增广路就退出。
正经的板子:
bool buildLevel()
{
std::queue<int> q;
memset(lv, , sizeof lv);
lv[S] = , q.push(S);
for (int i=; i<=T; i++) cur[i] = head[i];
for (int tmp; q.size(); )
{
tmp = q.front(), q.pop();
for (int i=head[tmp]; i!=-; i=nxt[i])
{
int v = edges[i].v;
if (!lv[v]&&edges[i].f < edges[i].c){
lv[v] = lv[tmp]+, q.push(v);
if (v==T) return true;
}
}
}
return false;
}
int fndPath(int x, int lim)
{
int sum = ;
if (x==T||!lim) return lim;
for (int i=cur[x]; i!=-&&sum <= lim; i=nxt[i])
{
int v = edges[i].v, val;
if (lv[x]+==lv[v]&&edges[i].f < edges[i].c){
if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f)))){
edges[i].f += val, edges[i^].f -= val;
sum += val;
}else lv[v] = -;
}
if (lim==sum) break; //小trick的效果是玄学致命的
}
cur[x] = head[x];
return sum;
}
int dinic()
{
int ret = , val;
while (buildLevel())
while ((val = fndPath(S, INF))) ret += val;
return ret;
}
初涉网络流[EK&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 ...
- 图论算法-最小费用最大流模板【EK;Dinic】
图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...
- 图论算法-网络最大流【EK;Dinic】
图论算法-网络最大流模板[EK;Dinic] EK模板 每次找出增广后残量网络中的最小残量增加流量 const int inf=1e9; int n,m,s,t; struct node{int v, ...
- 网络流小记(EK&dinic&当前弧优化&费用流)
欢 迎 来 到 网 络 瘤 的 世 界 什么是网络流? 现在我们有一座水库,周围有n个村庄,每个村庄都需要水,所以会修水管(每个水管都有一定的容量,流过的水量不能超过容量).最终水一定会流向唯一一个废 ...
- 初探网络流:dinic/EK算法学习笔记
前记 这些是初一暑假的事: "都快初二了,连网络流都不会,你好菜啊!!!" from 某机房大佬 to 蒟蒻我. flag:--NOIP后要学网络流 咕咕咕------------ ...
- HDU1532_Drainage Ditches(网络流/EK模板/Dinic模板(邻接矩阵/前向星))
Drainage Ditches Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- hiho一下,第115周,FF,EK,DINIC
题目1 : 网络流一·Ford-Fulkerson算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho住在P市,P市是一个很大很大的城市,所以也面临着一个 ...
- 网络流EK
#include <iostream> #include <queue> #include <string.h> #define MAX 302 using nam ...
随机推荐
- HTTP协议初识
HTTP协议介绍 当你在浏览器地址栏敲入“http://www.cnblogs.com/”,然后猛按回车,呈现在你面前的,将是博客园的首页了(这真是废话,你会认为这是理所当然的).作为一个开发者,尤其 ...
- Codeforces Round #565 (Div. 3) C. Lose it!
链接: https://codeforces.com/contest/1176/problem/C 题意: You are given an array a consisting of n integ ...
- Jmeter将JDBC Request查询结果作为下一个接口参数方法
现在有一个需求,从数据库tieba_info表查出rank小于某个值的username和count(*),然后把所有查出来的username和count(*)作为参数值,用于下一个接口. tieba_ ...
- NET Core迁移
向ASP.NET Core迁移 有人说.NET在国内的氛围越来越不行了,看博客园文章的浏览量也起不来.是不是要转Java呢? 没有必要扯起语言的纷争,Java也好C#都只是语言是工具,各有各的 ...
- Spring-boot原理(附带实现一个spring-boot-starter实例和代码下载)
(我就是个封面) Spring-boot自出现后,到现在火的很,大家貌似都在用,连招聘里面也要求会这个.但是说实话,spring-boot无外乎想实现一种可插拔的编程方式,说是简化配置,其实并没有 ...
- 点权生成树(gentree)
点权生成树(gentree) 题目背景 Awson是某国际学校信竞组的一只菜鸡.终于弄明白边权最小生成树后,然而又被大神嘲笑了.大神深邃的眼光中透露了些睿智,说道:“你会求点权最小生成树么?”Awso ...
- 使用jQuery发送ajax
我们先看一下js的原生ajax 直接进入代码,我们先准备好servlet,之后不会改动,所以先看一下. package cn.curry.servlet; import com.alibaba.fas ...
- <Android 应用 之路> 聚合数据SDK
聚合数据介绍 聚合数据是一个为智能手机开发者,网站站长,移动设备开发人员及图商提供原始数据API服务的综合性云数据平台.包含手机聚合,网站聚合,LBS聚合三部分,其功能类似于Google APIS.[ ...
- 在开发第一个Android应用之前需要知道的5件事:
你能否详细讲述一下,在开发Android应用过程中每一阶段要用到的技能和编程语言? 建立一个Android应用程序可以归结为两个主要技能/语言:Java和Android系统.Java是Android的 ...
- Centos 7 搭建git服务器及使用gitolite控制权限
一.安装git yum install git git --version #查看git版本 二.升级git(可选,如果之前已经安装git,需要升级git到最新版本) git clone https: ...