网络流入门
1. 最大流问题
残余网络:由残余容量(每条边上的容量与流量之差)构成的叫残余网络。
增广路定理:如果残余网络中不存在增广路,则当前流就是最大流。
最小割最大流定理:
最大流的流量 = 最小割的容量
1.1 EK算法(用BFS寻找增广路) O ( n m 2 ) O(nm^2) O(nm2)
思路:
寻找增广路,记录从s到每个节点的路径上的最小残量a[i],则a[t]就是s到t的最小残量。由于a[]始终是正数,所以用它代替了vis标志数组。
edges[e]与edges[e^1]互为反向弧
1.2 Dinic算法(用DFS在层次网络中多次增广) O ( n 2 m ) O(n^2m) O(n2m)
思路:
用BFS构建层次网络,用DFS进行多次增广,每进行一次增广后,就要重新构建BFS网络,直到s->t不存在路径。
层次图:在残量网络中,起点到节点u的高度叫做dis[u],我们把dis[u]看做节点u的层次,只保留每个点出发到下一个层次的弧,得到的图就叫做层次图。 这样,每条“起点->层次1->层次2->层次3->…”的路径都是s->t的最短路。
1.3 ISAP算法(在增广过程中完成层次网络的更新) O ( n 2 m ) O(n^2m) O(n2m)
思路:
Dinic算法中,我们需要每次搜索出层次图,而在ISAP中,我们只需要每次dfs的过程中修改距离标号。
具体来说,我们用d[x]表示残余网络上x到汇点t的最短距离,我们每次沿着d[x]=d[v]+1的路增广。如果点x的出边的点没有发现满足这个条件的那么就说明当前的最短距离d[x]已经过时了,我们需要修改距离标号。修改距离标号,就是让x可以至少有一个点可以增广,所以取所有d[v]中最小的那个加一即可。这样增广下去,当d[s],即s到t的距离大于等于n的时候,就说明至少有一个点经过了两次,即不存在增广路了,这个时候算法结束。
优化
- 如果一开始把距离标号都设为0,那么dfs最多需要O(n2)来把距离标号初始化,而我们可以最开始逆向bfs一次,O(n+m)初始化所有距离标号。
- GAP优化:如果距离标号出现了断层,那么显然不存在新的增广路。我们用一个num数组记录每种层次标号有多少个,如果当前修改了最后一个某种层次标号,那么就出现了前后断层,结束算法。
- 增广过程中,如果一个点的标号没有修改过,那么它已经遍历过的边不需要再遍历一次,所以我们存下每次遍历到哪条边,下一次从这条边cur[x]开始遍历(因为有可能到这里之后流量用完了,但是还没有增广完)。
- 最短路的修改具有连续性,即我们不需要每次求后继的标号最小值,而是直接给标号加一。
- 同Dinic中,如果流量用完了直接退出。
2. 最小费用最大流问题
思路:
类似EK算法,每次用Bellman-Ford算法而非BFS寻找增广路。
只要初始流是该流量下的最小费用可行流,每次增广后的新流都是新流量下的最小费用流。