一直以来都觉得网络流特别的高深莫测,所以就没有怎么用心看过。结果在学校校队选拔赛碰上了道网络流套路题,当场就跳过了。事后想想不行,不管怎么样的算法总得先去学才能会,于是看了几篇博客,姑且是按着博客里的介绍自己一行行打出来了。当然了,这里并不做什么再次讲解,纯粹丢个有注释的代码就跑了(方便以后忘了回来看眼)。对于网络流具体应该怎么理解,可以看这篇博客:https://blog.csdn.net/lzoi_hmh/article/details/74940366
首先,这是我按照网络流想法直接实现的一个非常朴素、低效的代码,见下:
1 | #include <cstdio> |
每次 dfs 都只增广一条增广路,找到汇点就直接退出,提交到 Codevs 1993 上,不出意外地 TLE 了。
对其稍加改进,增加分层、一次增广多条路径,也就是 dinic 算法,于是写成了这样:
1 | #include <cstdio> |
其实可以发现,大部分讲网络流的文章里都提到了增加反向弧、剩余流量、最大流量的概念。一开始我也是因为这些概念多,下意识认为实现起来特别麻烦就放弃了。其实利用链式前向星的存图方法的话,只要在加单向边的时候增加一条相反的、剩余流量为 0 的边,然后当作普通的边处理就可以了;互相找反向边也很方便,直接异或 1 就可以了。至于最大流量和剩余流量,只要确保正向边和反向边剩余流量都不为负即可,不用管最大流量。另外无向图建图时,给反向边也设置成与正向边相同的剩余流量就可以了。
另外最大流与最小割是可以互相转换的,但如果要知道最小割的边数就会显得有点麻烦。其实有一个办法,在 m * sigma ( ci ) 不会爆精度的情况下,可以在加边时让每条边的流量存为 ci * m + 1,最终 ans % m 就是最小割的边数,ans / m 就是答案。
当你在凝视深渊的时候 深渊也正在凝视着你