初涉网络流[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 ...
随机推荐
- angularJs 指令调用父controller某个方法
1.父级controller:例如有个 init() 方法; 父级与子级的通信数据是$scope.controlFlag={}; 那么可以在父级controller里这样写:$scope.contro ...
- python 合并两个文件并将合并内容保存在另一个文件中
简单地文件合并方法 思路如下: 分别读取两个文件中的内容,并将其保存在一个列表中,将列表通过join()函数转为字符,并将新字符保存在新的文件中. 其中,test1.txt中的内容为: test2.t ...
- jQuery EasyUI/TopJUI输入框事件监听
jQuery EasyUI/TopJUI输入框事件监听 代码如下: <div data-toggle="topjui-panel" title="" da ...
- 《SQL 进阶教程》 case:在 CASE 表达式中使用聚合函数
1.只加入一个社团的学生的社团id select std_id, max(club_id) from student_clubgroup by std_idhaving count(*) =1---- ...
- 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装
既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...
- spring boot 事务
spring事务:默认自动提交只读:@Transactional(readOnly = true)读写:@Transactional(),因为等同于@Transactional(readOnly = ...
- 简单记录下HTTPS中的SSL
大概思路 大概思路是混合加密的方式,即对称加密方式混合非对称加密方式. 非对称加密会更加安全,功能也更强大,但他复杂而且速度慢. 对称加密速度快,但要保证这个公共密钥的正确性和真实性. 所以两者结合, ...
- 047 Permutations II 有重复数字的全排列
给定一个可能包含重复数字的集合,返回所有可能的不同全排列.例如,[1,1,2] 有以下不同全排列:[ [1,1,2], [1,2,1], [2,1,1]] 详见:https://leetcode ...
- java exception "file not found or file not exist"
出现这种异常一般有两种原因,第一种就是文件真的不存在:第二种是权限问题,权限问题又分为文件本身的权限和包含它的文件夹的权限 比如 ~/aaa/bbb/ccc/ddd/eee.txt 只要 aaa , ...
- mysql主给备赋予权限时报错,MySQL [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause
https://www.cnblogs.com/skymyyang/p/7551646.html 在my.cnf 里面设置sql_mode='STRICT_TRANS_TABLES,NO_ZERO_I ...