1. 程式人生 > >最大流問題——Ford-Fulkerson方法的java實現

最大流問題——Ford-Fulkerson方法的java實現

使用的圖的資料結構是鄰邊雜湊表,見Graph原始碼。

package algorithm;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import matrix.Graph;

public class MaxFlow {

	/**
	 * 定義最後結果的結構: 源節點、匯節點、最大流量、產生流量的通路
	 */
	public int sink, source;
	public double maxFlow;
	public List<UnblockedPath> paths =
new LinkedList<>(); public MaxFlow(int s, int t) { source = s; sink = t; } /** * 可增廣路徑: 指流量未達到飽和的從源到匯的路徑,增加該路徑的流量使總流量增大 */ static class UnblockedPath { public boolean found = false; // 是否找到可增廣路徑 public int[] preNodes = null; // 用於回溯從終點到起點的路徑 public double flow = 0; // 該路徑上的流量 } /** * 用廣度優先的搜尋方式,找到可增廣路徑後立刻返回 * * @param g * 最大流問題所在的圖 * @param source * 源節點 * @param sink * 匯節點 * @return 找到一條可增廣路徑 */
public static UnblockedPath findUnblockedPath(Graph g, int source, int sink) { UnblockedPath ubp = new UnblockedPath(); // 初始化回溯節點 ubp.preNodes = new int[g.getNodeNum()]; ubp.preNodes[source] = -1; // 初始化源節點的最大可行流 boolean[] visited = new boolean[g.getNodeNum()]; Queue<Integer> queue =
new LinkedList<>(); queue.offer(source); while (!queue.isEmpty()) { int currentNode = queue.poll(); visited[currentNode] = true; // 如果找到了一條通向終點的路徑,立即返回 if (currentNode == sink) { ubp.found = true; break; } // 將當前節點的出邊相連的下一節點加入搜尋隊列,並記錄前驅 for (Integer nextNode : g.getOutEdges(currentNode).keySet()) { if (!visited[nextNode] && g.getEdge(currentNode, nextNode) > 0) { ubp.preNodes[nextNode] = currentNode; // 記錄前驅 queue.offer(nextNode); } } } return ubp; } /** * 每次找到可增廣路徑後,將該路徑的流量增至最大,修改路徑上的邊的容量,迴圈直到沒有可增長路徑,對應流量最大值 * * @param g * 最大流問題所在的圖 * @param source * 源節點 * @param sink * 匯節點 * @return 最大流問題的解 */ public static MaxFlow FordFulkerson(Graph g, int source, int sink) { MaxFlow mf = new MaxFlow(source, sink); double increment = Graph.INF; while (true) { UnblockedPath ubp = findUnblockedPath(g, source, sink); if (!ubp.found) { break; } // 回溯的終點 int end = ubp.preNodes[source]; // 計算路徑 ubp 上的流量,即總流量的增量 int temp = sink; while (ubp.preNodes[temp] != end) { increment = Math.min(increment, g.getEdge(ubp.preNodes[temp], temp)); temp = ubp.preNodes[temp]; } // 儲存結果 ubp.flow = increment; mf.maxFlow += increment; mf.paths.add(ubp); // 更新容量 temp = sink; while (ubp.preNodes[temp] != end) { g.addEdge(ubp.preNodes[temp], temp, g.getEdge(ubp.preNodes[temp], temp) - increment); g.addEdge(temp, ubp.preNodes[temp], g.getEdge(temp, ubp.preNodes[temp]) + increment); temp = ubp.preNodes[temp]; } // g.show(); } return mf; } /** * 結果展示 */ public void analyse() { System.out.println("source : " + source); System.out.println("sink : " + sink); System.out.println("maxFlow : " + maxFlow); for (UnblockedPath ubp : paths) { int temp = sink; int end = ubp.preNodes[source]; System.out.print(ubp.flow + "\t: "); while (ubp.preNodes[temp] != end) { System.out.print(temp + " => "); temp = ubp.preNodes[temp]; } System.out.println(source); } } }

來看一個小例子:

1 為源節點, 4為匯節點, 求1到4的最大流。
在這裡插入圖片描述

	public static void main(String[] args) {
		Graph g = new Graph(5, false); // 有向圖
		g.setDefaultValue(0.0); // 預設值為0,用零容量來表示不存在的邊
		double[][] triples = { 
				{ 1, 2, 40 }, 
				{ 1, 4, 20 }, 
				{ 2, 4, 20 }, 
				{ 2, 3, 30 }, 
				{ 3, 4, 10 } 
			};
		g.addEdges(triples);
		g.show();

		MaxFlow mf = MaxFlow.FordFulkerson(g, 1, 4);
		mf.analyse();
	}

執行結果為:

0.0 0.0 0.0 0.0 0.0 
0.0 0.0 40.0 0.0 20.0 
0.0 0.0 0.0 30.0 20.0 
0.0 0.0 0.0 0.0 10.0 
0.0 0.0 0.0 0.0 0.0 
source  : 1
sink    : 4
maxFlow : 50.0
20.0	: 4 <= 2 <= 1
20.0	: 4 <= 1
10.0	: 4 <= 3 <= 2 <= 1

最後摘錄一段非常精煉的cpp程式碼,以備參考。

/*****************************************
* 這段程式碼太TM精煉了,何忍不抄!!! 
******************************************/
#include <cstdio> 
#include <cstring> 
#include <cmath> 
#include <algorithm> 
using namespace std; 
int map[300][300]; 
int used[300]; 
int n,m; 
const int INF = 1000000000; 
int dfs(int s,int t,int f) { 
  if(s == t) return f; 
  for(int i = 1 ;i <= n ; i ++) { 
    if(map[s][i] > 0 && !used[i]) { 
      used[i] = true; 
      int d = dfs(i,t,min(f,map[s][i])); 
      if(d > 0) { 
        map[s][i] -= d; 
        map[i][s] += d; 
        return d; 
      } 
    } 
  } 
} 
int maxflow(int s,int t) { 
  int flow = 0; 
  while(true) {
    memset(used,0,sizeof(used)); 
    int f = dfs(s,t,INF);//不斷找從s到t的增廣路 
    if(f == 0)
    return flow;//找不到了就回去 
    flow += f;//找到一個流量f的路 
  } 
} 
int main() {
while(scanf("%d%d",&m,&n) != EOF) { 
  memset(map,0,sizeof(map)); 
    for(int i = 0; i < m ; i ++) { 
      int from,to,cap; 
      scanf("%d%d%d",&from,&to,&cap);
      map[from][to] += cap; 
    } 
    cout << maxflow(1,n) << endl; 
  } 
  return 0; 
}