主要还是板子

Edmonds-Karp

从S开始bfs,直到找到一条到达T的路径后将该路径增广,并重复这一过程。

在处理过程中,为了应对“找到的一条路径把其他路径堵塞”的情况,采用了建反向弧的方式来实现“反悔”过程。

这种“反悔”的想法和技巧值得借鉴。

  1. int maxFlow()
  2. {
  3. int ret = ;
  4. for (;;)
  5. {
  6. memset(f, , sizeof f);
  7. memset(bck, , sizeof bck);
  8. std::queue<int> q;
  9. f[S] = INF, q.push(S);
  10. for (int tmp; q.size(); )
  11. {
  12. tmp = q.front(), q.pop();
  13. for (int i=head[tmp]; i!=-; i=nxt[i])
  14. {
  15. int v = edges[i].v;
  16. if (!f[v]&&edges[i].f < edges[i].c){
  17. f[v] = std::min(f[tmp], edges[i].c-edges[i].f);
  18. bck[v] = i, q.push(v);
  19. }
  20. }
  21. if (f[T]) break;
  22. }
  23. if (!f[T]) break;
  24. for (int i=T; i!=S; i=edges[bck[i]].u)
  25. {
  26. edges[bck[i]].f += f[T];
  27. edges[bck[i]^].f -= f[T];
  28. }
  29. ret += f[T];
  30. }
  31. return ret;
  32. }

Dinic

EK的效率是$O(nm^2)$的,它把很多时间浪费在了重复的搜索上面。

dinic有如下两个重要的定义:

  • 层次$\text{level(x)}$:表示点$x$在层次图中与源点$S$的距离。
  • 层次图:在原来的残量网络当中,只保留所有可被增广的边以及与之相连的点。

bfs建出来的层次图对于接下去的dfs增广具有一种“指导”作用。使用了反向弧技巧,意味着不管用什么方法,只需要找到一条增广路就行。在这种情况下,我们来考虑dfs增广的优劣之处:一方面它一旦找到一条增广路就能快速退出,比bfs的逐级外扩更高效;另一方面纯粹的dfs受搜索顺序的影响很大,因为(可以像卡SPFA以及某些图论算法一样)挂一些诱导节点附带数量巨大的边,就能置dfs于死地。但是这里dfs依靠建出来的层次图,每次只向距离+1的点搜索。这意味着我们避免了对同一个节点的重复搜索,或是偏离T方向浪费时间。

  1. bool buildLevel()
  2. {
  3. memset(lv, , sizeof lv);
  4. std::queue<int> q;
  5. q.push(S), lv[S] = ;
  6. for (int i=; i<=T; i++) cur[i] = head[i];   //tip1
  7. for (int tmp; q.size(); )
  8. {
  9. tmp = q.front(), q.pop();
  10. for (int i=head[tmp]; i!=-; i=nxt[i])
  11. {
  12. int v = edges[i].v;
  13. if (!lv[v]&&edges[i].f < edges[i].c){
  14. lv[v] = lv[tmp]+, q.push(v);
  15. if (v==T) return true;        //tip2
  16. }
  17. }
  18. }
  19. return false;
  20. }
  21. int fndPath(int x, int lim)        //此处已更新,详情见下
  22. {
  23. if (x==T) return lim;
  24. for (int &i=cur[x]; i!=-; i=nxt[i])       //tip1
  25. {
  26. int v = edges[i].v, val;
  27. if (lv[x]+==lv[v]&&edges[i].f < edges[i].c){
  28. if ((val = fndPath(v, std::min(lim, edges[i].c-edges[i].f)))){
  29. edges[i].f += val, edges[i^].f -= val;
  30. return val;
  31. }else lv[v] = -;             //tip3  
  32. }
  33. }
  34. cur[x] = head[x];
  35. return ;
  36. }
  37. int dinic()
  38. {
  39. int ret = , val;
  40. while (buildLevel())
  41. while ((val = fndPath(S, INF))) ret += val;
  42. return ret;
  43. }

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提到的每次寻找到一条增广路就退出。

正经的板子:

  1. bool buildLevel()
  2. {
  3. std::queue<int> q;
  4. memset(lv, , sizeof lv);
  5. lv[S] = , q.push(S);
  6. for (int i=; i<=T; i++) cur[i] = head[i];
  7. for (int tmp; q.size(); )
  8. {
  9. tmp = q.front(), q.pop();
  10. for (int i=head[tmp]; i!=-; i=nxt[i])
  11. {
  12. int v = edges[i].v;
  13. if (!lv[v]&&edges[i].f < edges[i].c){
  14. lv[v] = lv[tmp]+, q.push(v);
  15. if (v==T) return true;
  16. }
  17. }
  18. }
  19. return false;
  20. }
  21. int fndPath(int x, int lim)
  22. {
  23. int sum = ;
  24. if (x==T||!lim) return lim;
  25. for (int i=cur[x]; i!=-&&sum <= lim; i=nxt[i])
  26. {
  27. int v = edges[i].v, val;
  28. if (lv[x]+==lv[v]&&edges[i].f < edges[i].c){
  29. if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f)))){
  30. edges[i].f += val, edges[i^].f -= val;
  31. sum += val;
  32. }else lv[v] = -;
  33. }
          if (lim==sum) break;        //小trick的效果是玄学致命的
  34. }
  35. cur[x] = head[x];
  36. return sum;
  37. }
  38. int dinic()
  39. {
  40. int ret = , val;
  41. while (buildLevel())
  42. while ((val = fndPath(S, INF))) ret += val;
  43. return ret;
  44. }

初涉网络流[EK&dinic]的更多相关文章

  1. [知识点]网络流之Dinic算法

    // 此博文为迁移而来,写于2015年2月6日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vrg4.html      ...

  2. [无效]网络流之Dinic算法

    // 此博文为迁移而来,写于2015年2月6日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vrg4.html UPDA ...

  3. 图论算法-最小费用最大流模板【EK;Dinic】

    图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...

  4. 图论算法-网络最大流【EK;Dinic】

    图论算法-网络最大流模板[EK;Dinic] EK模板 每次找出增广后残量网络中的最小残量增加流量 const int inf=1e9; int n,m,s,t; struct node{int v, ...

  5. 网络流小记(EK&dinic&当前弧优化&费用流)

    欢 迎 来 到 网 络 瘤 的 世 界 什么是网络流? 现在我们有一座水库,周围有n个村庄,每个村庄都需要水,所以会修水管(每个水管都有一定的容量,流过的水量不能超过容量).最终水一定会流向唯一一个废 ...

  6. 初探网络流:dinic/EK算法学习笔记

    前记 这些是初一暑假的事: "都快初二了,连网络流都不会,你好菜啊!!!" from 某机房大佬 to 蒟蒻我. flag:--NOIP后要学网络流 咕咕咕------------ ...

  7. HDU1532_Drainage Ditches(网络流/EK模板/Dinic模板(邻接矩阵/前向星))

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  8. hiho一下,第115周,FF,EK,DINIC

    题目1 : 网络流一·Ford-Fulkerson算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho住在P市,P市是一个很大很大的城市,所以也面临着一个 ...

  9. 网络流EK

    #include <iostream> #include <queue> #include <string.h> #define MAX 302 using nam ...

随机推荐

  1. angularJs 指令调用父controller某个方法

    1.父级controller:例如有个 init() 方法; 父级与子级的通信数据是$scope.controlFlag={}; 那么可以在父级controller里这样写:$scope.contro ...

  2. python 合并两个文件并将合并内容保存在另一个文件中

    简单地文件合并方法 思路如下: 分别读取两个文件中的内容,并将其保存在一个列表中,将列表通过join()函数转为字符,并将新字符保存在新的文件中. 其中,test1.txt中的内容为: test2.t ...

  3. jQuery EasyUI/TopJUI输入框事件监听

    jQuery EasyUI/TopJUI输入框事件监听 代码如下: <div data-toggle="topjui-panel" title="" da ...

  4. 《SQL 进阶教程》 case:在 CASE 表达式中使用聚合函数

    1.只加入一个社团的学生的社团id select std_id, max(club_id) from student_clubgroup by std_idhaving count(*) =1---- ...

  5. 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装

    既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...

  6. spring boot 事务

    spring事务:默认自动提交只读:@Transactional(readOnly = true)读写:@Transactional(),因为等同于@Transactional(readOnly = ...

  7. 简单记录下HTTPS中的SSL

    大概思路 大概思路是混合加密的方式,即对称加密方式混合非对称加密方式. 非对称加密会更加安全,功能也更强大,但他复杂而且速度慢. 对称加密速度快,但要保证这个公共密钥的正确性和真实性. 所以两者结合, ...

  8. 047 Permutations II 有重复数字的全排列

    给定一个可能包含重复数字的集合,返回所有可能的不同全排列.例如,[1,1,2] 有以下不同全排列:[  [1,1,2],  [1,2,1],  [2,1,1]] 详见:https://leetcode ...

  9. java exception "file not found or file not exist"

    出现这种异常一般有两种原因,第一种就是文件真的不存在:第二种是权限问题,权限问题又分为文件本身的权限和包含它的文件夹的权限 比如 ~/aaa/bbb/ccc/ddd/eee.txt  只要 aaa , ...

  10. 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 ...