让我先讲一个故事吧。

一些小精灵要准备从银月城(S)迁徙到Nibel山(T)。

这两个地方之间的道路构成了一个网络。

每个道路都有它自己的容量,这决定了每天有多少小精灵可以同时从这儿通过。

现在它们想知道,它们迁徙的速度最大是多少只每天。

这就是一道红果果的最大流问题。


在建图时,我们把每条边拆成2条,

它们方向相反,和原来那条边方向相同的边的容量还是原来的容量,

而另一条边的容量就设成0。

当我们要修改剩余容量的时候,

把正方向的边的容量减少,把反方向的边的容量增加,

就可以很方便的修改它了。


一种最朴实的算法是,

每次寻找一条可以从S到T的有可用容量剩余的路径(我们把它叫增广路,虽然我也不知道为什么是这个名字),

不断寻找到没有这种路径为止。

这种算法叫EK,复杂度是\(O(fm)\),f是得到的最大流,m是道路数量。

可见这个算法运行速度很垃圾,特别时遇到这种情况

              /> 次元壁1(999/999) __/     |     \__ (999/999)/        |        \>银月城        (1/1)         Nibel山\__      |      __/>(999/999)   \     v     /   (999/999)\> 次元壁2

寻找的增广路会一直穿过次元壁1和次元壁2来回走动

每次都只找到一条可以通过1只精灵的路

卡成gou bi


因此,我们提出了一个改进的算法:

把每一个点到起点S的距离用BFS求出来,得到一个分层图,

然后在寻找增广路的时候,限制当前点只能往比它距离刚刚好大1的点前进,

在找到一条增广路后,再用BFS更新分层图,继续寻找增广路,肛到你听到为止直到找不到为止。

我们把这个算法叫做Dinic,复杂度是\(O(min(Xuanxue,n^2 \times m))\),n是点数。不要问我Xuanxue是什么鬼

这个算法有一个优化,叫GAP优化,
主要就是把离起点S等于某个距离的点的数量记下来,
如果离起点S的距离等于某值的点全都不见了,
那么这个图就发生了断层,
再也找不到一条增广路出来。
这个时候就阔以提前结束程序哒。
有时候可以为程序提速100倍以上

有些人啊,发现这个狄尼克dinic算法有个大问题,

就是它每一次BFS的意义好像并不大,

毕竟出现改动的道路就那么点。

于是这些人就搞出了一个更快的算法:

isap

复杂度也许是\(O(0.8*(n^2 \times m))\)

和dinic最大的不同就是,它可以在寻找增广路的同时,自己更新分层图。

主要就是在找不到合法的增广路的时候(比如没有满足距离刚刚好大1的点),

就把现在这个点的距离设为离自己最近的出边的点的距离+1。

这里还有个优化叫当前弧优化,不过意义不大。

这是网上某个isap代码

int source;         // 源点
int sink;           // 汇点
int p[max_nodes];   // 可增广路上的上一条弧的编号
int num[max_nodes]; // 和 t 的最短距离等于 i 的节点数量
int cur[max_nodes]; // 当前弧下标
int d[max_nodes];   // 残量网络中节点 i 到汇点 t 的最短距离
bool visited[max_nodes];// 预处理, 反向 BFS 构造 d 数组
bool bfs()
{memset(visited, 0, sizeof(visited));queue<int> Q;Q.push(sink);visited[sink] = 1;d[sink] = 0;while (!Q.empty()){int u = Q.front();Q.pop();for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix){Edge &e = edges[(*ix)^1];if (!visited[e.from] && e.capacity > e.flow){visited[e.from] = true;d[e.from] = d[u] + 1;Q.push(e.from);}}}return visited[source];
}// 增广
int augment()
{int u = sink, df = __inf;// 从汇点到源点通过 p 追踪增广路径, df 为一路上最小的残量while (u != source){Edge &e = edges[p[u]];df = min(df, e.capacity - e.flow);u = edges[p[u]].from;}u = sink;// 从汇点到源点更新流量while (u != source){edges[p[u]].flow += df;edges[p[u]^1].flow -= df;u = edges[p[u]].from;}return df;
}int max_flow()
{int flow = 0;bfs();memset(num, 0, sizeof(num));for (int i = 0; i < num_nodes; i++) num[d[i]]++;int u = source;memset(cur, 0, sizeof(cur));while (d[source] < num_nodes){if (u == sink) {flow += augment();u = source;}bool advanced = false;for (int i = cur[u]; i < G[u].size(); i++){ Edge& e = edges[G[u][i]];if (e.capacity > e.flow && d[u] == d[e.to] + 1){advanced = true;p[e.to] = G[u][i];cur[u] = i;u = e.to;break;}}if (!advanced){ // retreatint m = num_nodes - 1;for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix)if (edges[*ix].capacity > edges[*ix].flow)m = min(m, d[edges[*ix].to]);if (--num[d[u]] == 0) break; // gap 优化num[d[u] = m+1]++;cur[u] = 0;if (u != source)u = edges[p[u]].from;}}return flow;
}

看起来isap代码量很大。

其实不然,isap可以写的很简单。

这是我的这一题的全部代码

#include<bits/stdc++.h>
using namespace std;
inline int gotcha()
{register int a=0,b=1,c=getchar();while(!isdigit(c))b^=c=='-',c=getchar();while(isdigit(c))a=a*10+c-48,c=getchar();return b?a:-a;
}
const int _ = 10002 , __ = 200002;
int to[__],ne[__],v[__],he[_]={0},ecnt=1;
void adde(int a,int b,int c){to[++ecnt]=b,v[ecnt]=c,ne[ecnt]=he[a],he[a]=ecnt;}
int n,m,S,T,dis[_],gap[_];
int dfs(int d,int flw)
{if(d==T || flw==0)return flw;int i,g,mid=n-1,los=flw;for(i=he[d];i;i=ne[i])if(v[i]>0){if(dis[d]==dis[to[i]]+1){g=dfs(to[i],min(los,v[i])),v[i]-=g,v[i^1]+=g,los-=g;if(dis[S]>=n)return flw-los;if(!los)break;}mid=min(mid,dis[to[i]]);}if(flw==los){if(--gap[dis[d]]==0)dis[S]=n;dis[d]=mid+1,gap[dis[d]]++;}return flw-los;
}
int isap(){int ans=0;gap[S]=n;while(dis[S]<n)ans+=dfs(S,1e9);return ans;}
int main()
{register int i,j,k,a;n=gotcha(),m=gotcha(),S=gotcha(),T=gotcha();for(i=1;i<=m;i++)j=gotcha(),k=gotcha(),a=gotcha(),adde(j,k,a),adde(k,j,0);printf("%d",isap());return 0;
}

我在这儿省去了累赘的BFS和retreat操作,

把它们简化到dfs中。

实测运行速度依然很快

且程序关键部分(链式前向星,dfs(),isap())只有约0.6KB

性价比极高啊


以下是博主抽风内容


我突然有个写伪代码的冲动

要不我就写在这儿吧,还可以增强记忆

大法师的工作
信息:现在在哪儿现在带领了多少精灵如果我现在已经到了Nibel山,或者我这里已经没有更多的精灵了,那么我就把现在我这儿的精灵数量汇报回去。现在我要记录这儿离银月城的可能的最短距离,以及我还有多少精灵还滞留在这儿。我要寻找可以走的道路。如果这条路还有精灵的容身之地,并且这条路的终点离我们这儿的距离差是1的话,我将派出一个大法师信使,让她去这条路的终点,而她带领精灵的数量是,这条路的剩余容量与我这儿还滞留的精灵数量的最小值,毕竟带多了没有用。等她把成功到达终点的精灵数量带回来,我就把仍滞留在这儿的精灵的数量记录一下,也把这条道路的容量修改一下。当我收到了紧急的停止通知(GAP优化),我将立刻把成功前往目的地的精灵的数量汇报回去。如果我这儿已经没有滞留的精灵了,那我就不用寻找道路了。除此之外,我还要更新这儿离银月城的可能的最短距离。当我没有把任何精灵送到Nibel山,我会考虑这儿的距离是不是有点问题。我会将这里从距离统计的计数君中抹除,如果已经没有和这儿距离一样的点,那么就散布紧急通知(GAP优化)。之后把这里的距离改成可能的最短距离,并且让计数君把这里加入距离统计中。最后,我将汇报从我这里成功到达Nibel山的精灵的数量。领主伊萨普的工作
没有信息我将不断地派遣大法师,让她带上许多的精灵,从银月城出发,统计成功到达Nibel山的精灵的数量,
直到收到紧急的停止通知(GAP优化)为止。

upd:做了狼抓兔子之后还是决定把bfs代码放上来

queue<int> q;
void bfs()
{memset(dis,-64,sizeof(dis)),memset(gap,0,sizeof(gap));while(!q.empty())q.pop();dis[T]=1,gap[1]=1,q.push(T);int i,a,b;while(!q.empty()){a=q.front(),q.pop();for(i=he[a];i;i=ne[i])if(dis[to[i]]<0)b=to[i],dis[b]=dis[a]+1,gap[dis[b]]++,q.push(b);}
}

转载于:https://www.cnblogs.com/finder-iot/p/8408868.html

让菜鸡讲一讲网络流(isap)相关推荐

  1. szu 寒训 day#3 ST表 和 LCA问题 附例题 菜鸡解法

    昨天我们讲述了树状数组今天我们来讲ST表(解决静态RMQ (Rang Minmum/Maximum Query)问题的数据结构) 假如说我们暴力去查询区间的最值得话每次操作都是O(n) 如果询问次数跟 ...

  2. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  3. 怎么在一里以外识别出一个菜鸡程序员

    作者 | Scott Shipp 译者 | 核子可乐 当我还是菜鸡程序员时,我看到资深程序员们写的代码会觉得:"这也太傻了吧,毫无特点."当我工作经验渐长后,才发现自己还是太年轻, ...

  4. 【“到此一游”系列】(菜鸡参加“美亚杯” 电子取证大赛感受)

    到此一游 -- 美亚杯 感受及经验 背景 比赛现场 1. 上午八点二十 2. 九点 3. 十一点 我的比赛过程 比赛经验 我的思路是 1. 第一步 2. 第二步 3.第三步 注 结尾 感受及经验 背景 ...

  5. 一个菜鸡的ACM之路

    花絮 做为一个大三计软狗,那么菜,打完19年的区域赛,也没时间打20年的区域赛了吧.这个学期,是我的第一次,也是最后一次,打CCPC和ICPC,忙前忙后,共打了3场:打完后,又去赶各种死亡DDL:DD ...

  6. 【实习之路】投了109份简历,幸运的大二菜鸡终于拿到欢聚时代的offer了!!

    文章目录 前言 正文 面试过程 笔试情况 一轮面试: 二轮面试: 三轮面试: 发放offer 总结 前言 我是一名普通3本(本地叫2本,外地叫3本)的大二(准大三)学生,学校是一个民办独立院校,软件工 ...

  7. 转专业菜鸡的秋招总结

    基础不牢 地动山摇 重拾基础 春招再战 写在前面 阿宾的高中成绩并不理想 现实不像小说 现实往往比小说更加精彩 进入12月,秋招基本收官,在结束了11月末的最后一场面试后,回顾总结了秋招的坎坷经历,悔 ...

  8. 1个系统节拍 c语言_【菜鸡C语言】菜鸡鼓起勇气用Dev-c++打起节奏来了

    动次打次动次打次,打好节奏后,要进一步升华!学C语言这么久,不想搞点事情? 本文中纯属原创,未经授权禁止转载 本篇幅巨长,非专业人士可直接下拉至结尾看成果,然后点个赞¬∨¬ 学编程的一定不陌生,大多数 ...

  9. 20杭电计算机专业考研科目,20杭电计算机菜鸡跨考经验贴

    20杭电计算机菜鸡跨考经验贴 心路历程 初试 复试 第一次写帖子,记录一下这一年多的努力成功上岸杭电,同时也是延续一下传统,去年也是在csdn和王道论坛上看到学长学姐的各种经验帖,从中得到很多重要的信 ...

  10. 【菜鸡读论文】Face2Exp: Combating Data Biases for Facial Expression Recognition

    [菜鸡读论文]Face2Exp: Combating Data Biases for Facial Expression Recognition 最近上海开始降温了,而且常常下雨,天气开始变得好冷啊! ...

最新文章

  1. python实训收获_英泰移动通信:学习Python心得体会
  2. 免费!「神策 2021 数据驱动大会」西安大数据技术专场来袭
  3. C++new和delete实现原理(汇编解释)
  4. 老铁,邀请你来免费学习人工智能!!!
  5. php 文件 后缀,php如何修改文件后缀名
  6. atom python教程_对于新手来说 如何用atom搭建python的ide?
  7. 分布式与集群(一):我眼中的分布式与集群
  8. 如何反编译APK文件
  9. 我有你没有游戏例子100_50米的决赛圈里面藏着100个人?光子:知道什么叫质量局了吧!...
  10. Pointer is missing a nullability type specifier (__nonnull or __nullable)
  11. 算法(第四版)IDEA终极环境配置
  12. 01_01_三步开启C语言编程_小白篇
  13. 2019牛客多校赛第8场 D Distance 三维树状数组
  14. 网页打开慢升级服务器宽带,网速快打开网页慢怎么办_网络测速很快但是上网很慢如何解决-win7之家...
  15. 成都传智播客,学生老师共同举杯庆祝初战高胜!
  16. Lab3:自行车码表
  17. vss2005 配置详解
  18. 最近在听周华健的歌曲
  19. PythonC++相互混合调用编程全面实战-03ctypes类型对应
  20. C语言学习笔记08-2素数判断专题篇

热门文章

  1. 首届 “女生科技体验节” 大爆料!
  2. 51单片机数控电源c语言设计,单片机数控电源设计,含源代码,原理图
  3. 在WINDOWS 2003上运行Apache服务
  4. Java开发笔记(六十三)双冒号标记的方法引用
  5. Modelsim调用用do脚本自动化仿真
  6. uitableview有个属性值 separatorStyle 可用来去掉cell间的白线:
  7. c# json转换实例
  8. 神奇的margin之豆瓣豆瓣么么哒
  9. Jquery小菜鸟---防止按钮在短时间内被多次点击
  10. 【翻译】Windows下文件的命名