先简单看一下主过程:
int MAXFLOW(){ hights(); prepare(); while (!Q.empty()) { u = Q.get; for each e in G ' (from u) push(e); if (!fixed(u)) reCalc(u); }}
接下来介绍算法 预流推进算法给每一个顶点一个标号h(v),表示该点到t的最短路(在残量网络中)。 第一步hights()过程,就是BFS出初始最短路,计算出每一个顶点的h(v)。
预流推进算法的特征是运用了预流来加快运算。预流说明图中的节点(除s, t),仅需要满足流入量 >= 流出量。其中流入量>流出量的接点,我们称之为活动节点。我们的算法就是不断地将活动结点,变为非活动结点,使得预流成为可行流。
算法过程prepare(),即首先将与s相连的边设为满流,并将这时产生的活动结点加入队列Q。这是算法的开始。 以后便重复以下过程直到Q为空: (1).选出Q的一个活动顶点u。并依次判断残量网络G'中每条边(u, v),若h(u) = h(v) + 1,则顺着这里条边推流,直到Q变成非活动结点(不存在多余流量)。(Push推流过程) (2).如果u还是活动结点。则需要对u进行重新标号:h(u) = min{h(v) + 1},其中边(u,v)存在于G' 中。然后再将u加入队列。(reCalc过程)
可以证明,通过以上算法得到的结果就是最大流。 如果该算法的Q是标准的FIFO队列,则时间复杂度为(n2m),如果是优先队列,并且标号最高的点优先的话:我们就得到了最高标号预流推进算法,其时间复杂度仅为(n2sqrt(m)),算是比较快的最大流算法了。
根据上面的伪代码, 实现算法如下
const int size = 501;const int MAX = 1 << 15;int graph[size][size];int label[size]; //标号bool visited[size];bool bfs(int st, int ed){ memset(label, -1, sizeof(label)); memset(visited, false, sizeof(visited)); label[st] = 0; visited[st] = true; vector < int >plist; plist.push_back(st); while (plist.size()) { int p = plist[0]; plist.erase(plist.begin()); for (int i = 0; i < size; i++) { if (graph[i][p] > 0 && !visited[i]) { plist.push_back(i); visited[i] = true; label[i] = label[p] + 1; } } } if (label[ed] == -1) { return false; } return true;}int inflow[size]; //流入量int maxFlow(){ memset(inflow, 0, sizeof(inflow)); //hights bfs(size - 1, 0); //end point: size - 1, start point: 0 memset(visited, false, sizeof(visited));//prepare() vector < int >plist; for (int i = 0; i < size; i++) { if (graph[start][i] > 0) { inflow[i] = graph[start][i]; graph[start][i] -= inflow[i]; graph[i][start] += inflow[i]; if (!visited[i]) { plist.push_back(i); visited[i] = true; } } } while (plist.size()) { int p = plist[0]; plist.erase(plist.begin()); visited[p] = false; int minLabel = -1; for (int i = 0; i < size; i++) { if (graph[p][i] > 0) { if (label[p] == label[i] + 1) { int flow = min(inflow[p], graph[p][i]); inflow[p] -= flow; inflow[i] += flow; graph[p][i] -= flow; graph[i][p] += flow; if (!visited[i] && inflow[i] > 0) { plist.push_back(i); visited[i] = true; } } } } if (inflow[p] > 0 && p != end) { for (int i = 0; i < size; i++) { if (graph[p][i] > 0) { if (minLabel == -1 || minLabel > label[i] + 1) { minLabel = label[i] + 1; } } } if (!visited[p] && minLabel != -1 && minLabel < size) //minLabel < size, 这个条件需要加上, 因为经过测试发现有死循环的可能 { for (int i = 0; i < size; i++) { if (label[i] + 1 == minLabel && graph[p][i] > 0) { visited[p] = true; label[p] = minLabel; plist.push_back(p); break; } } } } } return inflow[end];}