首页 > 代码库 > 《数据结构与算法分析:C语言描述》复习——第九章“图论”——最大流问题

《数据结构与算法分析:C语言描述》复习——第九章“图论”——最大流问题

2014.07.04 20:43

简介:

  给定一个有向图,如果每一条边u->v代表一根水管,定义能从u流向v的最大水流量。那么选定起点s和终点t,如果让水从s流入,从t流出,最多能达到多少流量而不挤爆水管?

图示:

  Capacity是容量的意思,那么我们用C(u, v)来表示u->v这条边,也就是u流向v的最大流量。

  下面是一张容量图:

  

  Ford-Fulkerson方法的思路:每次从图中找出一条增广路径,直到找不出来为止。

  什么是增广路径呢?是一条从起点s到终点t的权值大于0的路径。比如这条:

  

  为什么这条路径的流量是2?因为我们只能去路径上的最小容量,这样水管才不会挤爆。

  找到增广路径之后怎么办呢?我们把沿路的边扣除掉对应的流量,并增加一条反向的边(最大流的需要证明的地方就在这儿了,问题就是:最大流与最小割等价)。

  目前我们已经有了2单位的流量,流量图也发生了变化:

  

  继续寻找增广路径,比如找到一条路径:s->a->b->d->t:

  

  找到以后,增加流量,修改流量图:

  

  继续找到下一条增广路径:s->b->a->d->t,流量为1。

  增加1单位流量,并且修改流量图。

  

  继续找到一条增广路径:s->b->d->t,流量为1。

  增加1单位流量,并且修改流量图。

  

  到此为止,没有更多从s到t的增广路径了。总共的流量是2+1+1+1=5。

  因此最大流量为5。

  在这样的流量模型里,s作为入口,t作为出口,其他顶点的流入和流出一定会保持平衡。这仅仅是最简单的一种模型,如果允许多个入口或者多个出口的话,那么问题的复杂程度就不是几句话能说清楚的了。

  Ford-Fulkerson方法是基于这种增广路径思想的解法,用于处理这类问题。之所以叫“方法”而不是“算法”,是因为如何寻找增广路径并没有具体说明。

  那到底怎么找呢?可以用BFS、DFS、排序等等手段来组合使用。简单的来说,就是靠搜。根据寻找增广路径的方法不同,就有了几种不同的对应算法。

实现:

我的实现版本,使用BFS来寻找增广路径,所以属于Edmonds-Karp算法。

  1 // My first practice on maximum flow problem. This is an implementation using Edmonds-Karp Algorithm.  2 #include <algorithm>  3 #include <iostream>  4 #include <queue>  5 #include <vector>  6 using namespace std;  7   8 const int INFINITY = 1000000000;  9  10 bool findAugmentPath(const vector<vector<int> > &rg, vector<int> &path,  11     const int start, const int end) 12 { 13     // Find an augment path with BFS. 14     queue<int> q; 15     vector<int> back_trace; 16     int n; 17     int i; 18      19     n = rg.size(); 20     back_trace.resize(n, -1); 21      22     int tmp; 23      24     back_trace[start] = start; 25     q.push(start); 26     while (!q.empty()) { 27         if (back_trace[end] >= 0) { 28             // An augment path is found. 29             break; 30         } 31         tmp = q.front(); 32         q.pop(); 33         for (i = 0; i < n; ++i) { 34             if (rg[tmp][i] == INFINITY) { 35                 continue; 36             } 37             if (i == tmp || back_trace[i] >= 0) { 38                 continue; 39             } 40             back_trace[i] = tmp; 41             q.push(i); 42         } 43     } 44      45     if (q.empty()) { 46         return false; 47     } 48      49     path.clear(); 50     tmp = end; 51     while (tmp != start) { 52         path.push_back(tmp); 53         tmp = back_trace[tmp]; 54     } 55     path.push_back(tmp); 56     reverse(path.begin(), path.end()); 57      58     while (!q.empty()) { 59         q.pop(); 60     } 61     back_trace.clear(); 62      63     return true; 64 } 65  66 void addFlow(vector<vector<int> > &rg, const vector<int> &path, const int flow) 67 { 68     int i; 69      70     for (i = 0; i < (int)path.size() - 1; ++i) { 71         if (rg[path[i]][path[i + 1]] == flow) { 72             rg[path[i]][path[i + 1]] = INFINITY; 73         } else { 74             rg[path[i]][path[i + 1]] -= flow; 75         } 76          77         if (rg[path[i + 1]][path[i]] == INFINITY) { 78             rg[path[i + 1]][path[i]] = flow; 79         } else { 80             rg[path[i + 1]][path[i]] += flow; 81         } 82     } 83 } 84  85 int maximumFlow(const vector<vector<int> > &graph, int start, int end) 86 { 87     // The residual graph 88     vector<vector<int> > rg; 89     vector<int> path; 90     int flow, maximum_flow; 91     int i; 92      93     if (graph.size() < 2 || start == end) { 94         return 0; 95     } 96      97     rg = graph; 98     maximum_flow = 0; 99     while (findAugmentPath(rg, path, start, end)) {100         flow = rg[path[0]][path[1]];101         for (i = 1; i < (int)path.size() - 1; ++i) {102             flow = min(flow, rg[path[i]][path[i + 1]]);103         }104         addFlow(rg, path, flow);105         maximum_flow += flow;106     }107     108     return maximum_flow;109 }110 111 int main()112 {113     vector<vector<int> > graph;114     vector<int> dist;115     vector<bool> reachable;116     int n;117     int nk;118     int i, j;119     int tmp, tmp_dist;120     int maximum_flow;121     int start, end;122     123     while (cin >> n && n > 0) {124         graph.resize(n);125         for (i = 0; i < n; ++i) {126             graph[i].resize(n, INFINITY);127         }128         129         for (i = 0; i < n; ++i) {130             cin >> nk;131             for (j = 0; j < nk; ++j) {132                 cin >> tmp >> tmp_dist;133                 graph[i][tmp] = tmp_dist;134             }135         }136         137         cin >> start >> end;138         maximum_flow = maximumFlow(graph, start, end);139         140         cout << "Maximum flow is " << maximum_flow << "." << endl;141         142         for (i = 0; i < n; ++i) {143             graph[i].clear();144         }145         graph.clear();146     }147     148     return 0;149 }