最大流首次体验感受——

 

什么是最大流呢?

从一个出发点(源点),走到一个目标点(汇点),途中可以经过若干条路,每条路有一个权值,表示这条路可以通过的最大流量。

最大流就是从源点到汇点,可以通过的最大流量。

接下来我们看一个图——

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图1

这个图中,s是源点,t是汇点。期间可以经过2, 3, 4, 5, 6几个点。每条边上有两个权值,其中第一个表示当前通过这条边的流量,第二个表示这条边最大可以通过的流量。

最佳情况,即最大可以通过的流量的一种情况是这样的——

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图2

但还有一种情况,同样可以达到最大流——

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图3

这里得到两个结论:

1. 每条路径中都有至少一条边是满的。

2. 最大流可能不止一种情况。

 

 

接下来我们再看另一个图:

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图4

如果在这个图上找最大流该怎么找?

这样?

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图5

不对,这个图乍一看好像满足每条路径上都有一个满流的边这个条件,但是其实还有更大的流——

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图6

怎么办呢?

我们可以通过这个方式从图5变到图6——

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93图7

这里我们的可以这样理解,在我们走出图5 的结果以后,我们允许图中出现图7中的绿色的边,然后我们就得到了绿色的数字所标示出的一条新路,通过这条路径,我们就获得了最大流。

如果我们获得了一个流量图,这个流量图中每条路径上都有一条边是满流了,如何判断这是不是一个最大流的图呢?通过上面的方法,我们在通过某条边之后,在这两个点之间构造一条反向的并且和通过的流量大小相同的边(称为反向边)。这样,就可能产生一条新路,使整个图中的流量增加。那么,我们不断地构造这种边,直到无法寻找到新的路径为止(称为增广路径),是不是就得到了最大流呢?

总结起来,每次找到一条增广路,增广路中每条边的值,都减去路径中,边值最小的边的值(读起来很凹口是不是?多读几遍就好了)。同时,还要给每条边都加上反向边。重复寻找,直到找不到新的路径,我们就获得了这个图的最大流。

注意,

  1. 每次寻找增广路径后,我们都会将原图更改,这样,我们会得到一个新的图。
  2. 在获得最大流的图之前,我们获得的每张图都称为残余网络。原始图也可以视为残余网络。

 

以上讲的是寻找最大流的思想。

但是,寻找增广路径的方法不止一种。

 

我最直接想到的方法,使用dfs多次搜索这张图,直到找不到为止。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int N = 210;
 8 const int M = 10000010;
 9 
10 int n, m;
11 int mp[N][N];               //保存地图信息,有向图
12 bool vis[N][N];             //dfs时标记使用
13 int ans;                    //最终结果
14 
15 void init()
16 {
17     memset(mp, 0, sizeof(mp));
18     memset(vis, 0, sizeof(vis));
19     for(int i = 0; i < n; i++)
20     {
21         int a, b, c;
22         scanf("%d%d%d", &a, &b, &c);
23         mp[a][b] += c;
24     }
25     ans = 0;
26 }
27 
28 int dfs(int x, int maxn)
29 {
30     if(x == m)
31     {
32         ans += maxn;        //每次走到汇点后增加的流量
33         return maxn;
34     }
35     int flow = 0;
36     for(int i = 1; i <= m; i++)
37     {
38         if(mp[x][i] > 0 && !vis[x][i])
39         {
40             vis[x][i] = 1;
41             int mmaxn = maxn < mp[x][i] ? maxn : mp[x][i];      //取本路径中所可以通过的最小值
42             int mid;
43             if(mmaxn > 0) mid = dfs(i, mmaxn);                  //如果此路仍然是通路,则继续搜索
44             if(mid > 0)
45             {
46                 mp[x][i] -= mid;                        //已经经过的路要减去耗费的流量
47                 mp[i][x] += mid;                        //反向路(弧)增加耗费的流量
48                 maxn -= mid;                            //走过一条通路后剩余的流量
49                 flow += mid;                            //已经消耗的流量
50                 if(maxn == 0) break;
51             }
52         }
53     }
54     return flow;
55 }
56 
57 int main()
58 {
59     //freopen("test.in", "r", stdin);
60     while(~scanf("%d%d", &n, &m))
61     {
62         init();
63         while(dfs(1, M) > 0) memset(vis, 0, sizeof(vis));
64         printf("%d\n", ans);
65     }
66     return 0;
67 }
dfs

相关文章: