自己学习《图论》的基础算法,终于走到最大流的地盘了 呵呵……我可是足足整了三天啊!!!

     开始看ppt,讲述Ford_Fulkerson,尽管这个算法已经被更加优秀的 Edmonds-Karp算法所取代。我研究这个算法的目的其实在于 Edmonds-Karp算法,没办法,谁叫 Edmonds-Karp算法是由Ford_Fulkerson改进过来的呢!

   把ppt看懂过后就自己上代码了。结果真的是让人崩溃:有向图,流竟然可以回退!意味着有向图必须用无向图的思路来考虑!

    我自己写代码,优先考虑的是DFS。因为我觉得既然是从起点到汇点。DFS比较快嘛!我傻糊乎乎的照搬ppt上面的实现过程,把边提取出来,用一个类记录边的信息,然后用DFS去搜索,边搜边改。导致的结果是我连续改版了8次,一步步跟踪调试,当然注定是失败的!尽管最后都没成功,却让我一点一点清晰地理解了这个算法的实现过程! 最后决定研究别人的代码了,不管三七二十一,先把代码贴出来吧! 希望不会有盗用代码之嫌!

   
   
   
   
  1. package com.xh.Ford_Fulkerson;  
  2.  
  3. import java.util.LinkedList;  
  4. import java.util.Queue;  
  5. import java.util.Scanner;  
  6.  
  7. /*  
  8.  * 6 10    // 6 nodes, 10 edges  
  9.  0 1 16  // capacity from 0 to 1 is 16  
  10.  0 2 13  // capacity from 0 to 2 is 13  
  11.  1 2 10  // capacity from 1 to 2 is 10  
  12.  2 1 4   // capacity from 2 to 1 is 4  
  13.  3 2 9   // capacity from 3 to 2 is 9  
  14.  1 3 12  // capacity from 1 to 3 is 12  
  15.  2 4 14  // capacity from 2 to 4 is 14  
  16.  4 3 7   // capacity from 4 to 3 is 7  
  17.  3 5 20  // capacity from 3 to 5 is 20  
  18.  4 5 4   // capacity from 4 to 5 is 4   
  19.  */ 
  20. public class Ford_Fulkerson09 {  
  21.  
  22.     private int capacity[][];  
  23.     private int flow[][];  
  24.     private boolean visited[];  
  25.     private int pre[];//通过pre记录了路径  
  26.     private int nodes;  
  27.     static int count=0;  
  28.     static int[][] map = { { 02930 },// 三条边  
  29.             { 00708 },// 两条边  
  30.             { 06040 },// 两条边  
  31.             { 00005 },// 一条边  
  32.             { 00000 } //  
  33.     };  
  34.  
  35.     public Ford_Fulkerson09(int[][] capacity, int nodes) {  
  36.         this.capacity = capacity;  
  37.         this.nodes = nodes;  
  38.         this.flow = new int[nodes][nodes];  
  39.         this.pre = new int[nodes];  
  40.         this.visited = new boolean[nodes];  
  41.     }  
  42.  
  43.     public int maxFlow(int src, int des) {  
  44.         int maxFlow = 0;  
  45.  
  46.         for (int i = 0; i < nodes; i++)  
  47.             for (int j = 0; j < nodes; j++)  
  48.                 flow[i][j] = 0;  
  49.  
  50.         while (true)// find a augment path  
  51.         {  
  52.             for (int i = 0; i < nodes; i++) {  
  53.                 visited[i] = false;  
  54.             }  
  55.             pre[src] = -1;  
  56.  
  57.             if (!BFS(src, des)) {// the BFS  
  58.                 System.out.println("break::::"+count);  
  59.                 break;  
  60.             }  
  61.  
  62.             /*  
  63.              * DFS(src,des);//DFS if(!visited[des]) break;  
  64.              */ 
  65.  
  66.             int increment = Integer.MAX_VALUE;  
  67.               
  68.             System.out.println("path:" +(count++));  
  69.             for (int i = des; pre[i] >= src; i = pre[i]) {  
  70.                 // find the min flow of the path  
  71.                 increment = Math.min(increment, capacity[pre[i]][i]  
  72.                         - flow[pre[i]][i]);  
  73.                 System.out.print(i+",");  
  74.             }  
  75.             System.out.print(0+",");  
  76.             System.out.println();  
  77.             // update the flow  
  78.             //对记录的路径的处理从终点开始,到起点 这里的处理是很巧的  
  79.             for (int i = des; pre[i] >= src; i = pre[i]) {  
  80.                 flow[pre[i]][i] += increment;  
  81.                 flow[i][pre[i]] -= increment;//由于需要考虑到退回边,所以这里需要减少  
  82.                 /*不能不惊叹于这种处理   本来我们的流是没有考虑负值的  
  83.                  * 而且的话,向前流的回退流需要两种不同的处理,而这里作者直接让回退流为负  
  84.                  * 这样的话,在bfs访问的过程中就不需要考虑两个点边的方向,这样就可以当作无  
  85.                  * 向图的来处理了*/ 
  86.             }  
  87.             // increase the maxFow with the increment  
  88.             System.out.println("==============flow==================");  
  89.             for (int i = 0; i < flow.length; i++) {  
  90.                 for (int j = 0; j < flow.length; j++) {  
  91.                     System.out.print(flow[i][j]+"   ");  
  92.                 }  
  93.                 System.out.println();  
  94.             }  
  95.             System.out.println("increment======="+increment+"============");  
  96.             maxFlow += increment;  
  97.         }  
  98.  
  99.         return maxFlow;  
  100.     }  
  101.  
  102.     private boolean BFS(int src, int des) {  
  103.         Queue queue = new LinkedList();  
  104.         queue.add(src);  
  105.         visited[src] = true;  
  106.         System.out.print(queue.peek()+",");  
  107.         while (!queue.isEmpty()) {  
  108.             int node = queue.poll();  
  109.               
  110.             for (int i = 0; i < nodes; i++) {  
  111.                 if (!visited[i] && (capacity[node][i] - flow[node][i] > 0)) {// 只要满足条件的点都加进去,而不会考虑两个点的边是正向边还是回退边  
  112.                     /*  
  113.                      * capacity[node][i] - flow[node][i] > 0  
  114.                      * 如果刚开始,回退边的flow是0,capacity也是0,不会被选择  
  115.                      * 但是回退边的flow会被设为负值,所以后来遇到回退边的时候, 回退边就会被考虑进来了 尽管capacity为0  
  116.                      */ 
  117.                     queue.add(i);  
  118.                     visited[i] = true;  
  119.                     pre[i] = node;// record the augment path  
  120.                     System.out.print(i+",");  
  121.                 }  
  122.             }  
  123.         }  
  124.         System.out.println("======================pre"+count+"================");  
  125.         for (int i = 0; i < pre.length; i++) {  
  126.             System.out.print(pre[i]+",");  
  127.         }  
  128.         System.out.println();  
  129.         return visited[des];  
  130.     }  
  131.  
  132.     public static void main(String[] args) {  
  133.         int nodes;  
  134.         nodes = map[0].length;  
  135.         int[][] capacity = new int[nodes][nodes];  
  136.  
  137.         for (int i = 0; i < nodes; i++) {  
  138.             for (int j = 0; j < nodes; j++) {  
  139.                 if (map[i][j] != 0) {  
  140.                     capacity[i][j] = map[i][j];  
  141.                 }  
  142.             }  
  143.         }  
  144.  
  145.         Ford_Fulkerson09 maxFlow = new Ford_Fulkerson09(capacity, nodes);  
  146.         System.out.println(maxFlow.maxFlow(0, nodes - 1));  
  147.     }  
  148. }  

由于代码中加入了打印输出,所以显得有点乱。 如果研究,最好先把里面大量的输出去掉。原图如下

对最大流算法Ford_Fulkerson的研究与理解_第1张图片 

     当然这个图用矩阵的方式来存储,其实由于开始看图论是看matlab的原因,习惯了矩阵。

这个程序的实现有以下几点我觉得是很值得赞赏的:

      采用BFS搜索,只记录可以实现的路径。而且在对路径的处理

// update the flow
   //对记录的路径的处理从终点开始,到起点 这里的处理是很巧的
   for (int i = des; pre[i] >= src; i = pre[i]) {
    flow[pre[i]][i] += increment;
    flow[i][pre[i]] -= increment;//由于需要考虑到退回边,所以这里需要减少
    /*不能不惊叹于这种处理   本来我们的流是没有考虑负值的
     * 而且的话,向前流的回退流需要两种不同的处理,而这里作者直接让回退流为负
     * 这样的话,在bfs访问的过程中就不需要考虑两个点边的方向,这样就可以当作无
     * 向图的来处理了*/
   }

然后就是为流量的变化建立了一个跟capacity一样大小的矩阵。当然空间开销有点大。

再就是bfs实现的细节。当时我自己的考虑纠结在有向图边方向的问题上了。他这里直接绕过去了……给人的感觉好像根本就不用考虑边的方向,甚至都不用考虑边!只要满足:流量<容量 这个条件就够了!而其实,这个条件包含了以下的隐藏的条件:比如:两点一定会有边的,因为有边才有流量。如果是回退流,那当然其容量为0,但只要流量为负就ok了!

当然,我觉得在BFS搜索的过程中,其实做了许多无用的搜索。 差不多是每个点必须搜索到,如果汇点搜索不到的话程序就可以结束了 呵呵!