// CS 1501 // Demonstration of Edmonds and Karp PFS implementation of Ford-Fulkerson method // of calculating network flow. This implementation is done using an adjacency // matrix, and is using PFS to determine each augmenting path. The implementation // is based on the textbook PFS for adjacency matrices, with some modifications to // make it more readable. import java.io.*; import java.util.*; import javax.swing.*; public class Flow { private int V, E, source, sink; private int [][] size; private int [][] flow; int [] val; int [] dad; boolean [] intree; // Array to determine if vertex is in tree or not. // This frees the val[] array from its dual purpose // in the original text PFS algorithm (store weights // and maintain the fringe) int DEBUG = 0; public void getData() throws IOException { Scanner inScan; String ifname = JOptionPane.showInputDialog("Input File?"); inScan = new Scanner(new File(ifname)); // Note: the input is done here in a PRIMITIVE way, relying not on the // code symbols (ex. p, n, a) of the file but rather on the sequence of // lines. To make it more robust, you'd probably want to improve this, // but if you do it the same way as here, it should work as long as the // files are formatted in the exact same way inScan.nextLine(); inScan.nextLine(); inScan.next(); inScan.next(); V = inScan.nextInt(); E = inScan.nextInt(); inScan.next(); source = inScan.nextInt(); inScan.next(); inScan.next(); sink = inScan.nextInt(); inScan.next(); size = new int[V+1][V+1]; flow = new int[V+1][V+1]; val = new int[V+1]; dad = new int[V+1]; intree = new boolean[V+1]; int j, x, y, w; for (x = 1; x <= V; x++) for (y = 1; y <= V; y++) { size[x][y] = 0; flow[x][y] = 0; } for (j = 1; j <= E; j++) // initialize capacities based on { // the edge weights read in inScan.next(); x = inScan.nextInt(); y = inScan.nextInt(); w = inScan.nextInt(); size[x][y] = w; size[y][x] = -w; } } // Debugging code to show the matrices. public void showmat() { for (int i = 1; i <= V; i++) System.out.print(" " + i + " "); System.out.println(); for (int i = 1; i <= (8*V); i++) System.out.print("="); System.out.println(); for (int i = 1; i <= V; i++) { for (int j = 1; j <= V; j++) System.out.print("(" + size[i][j] + "," + flow[i][j] + ") "); System.out.println(); } } // Similar to the PFS MST algorithm from the text, we are using the val array // here as a priority queue to decide which vertex will be added to the (maximum) // spanning tree next. When the vertex added to the tree is the sink, the // augmenting path is complete. If the sink is not reached, no augmenting // path exists public boolean bestaugpath(int source, int sink) { int k, t, max = 0; // Since the val array is no longer concerned with designating the fringe, // it is now initialized to 0 for all vertices (compare to PFS in text) // It will store the edge weights used to choose the next vertex in the tree for (k = 1; k <= V; k++) { val[k] = 0; dad[k] = 0; intree[k] = false;} val[0] = 0; val[source] = Integer.MAX_VALUE; for (k = source; k != 0; k = max, max = 0) { if (DEBUG == 1) { System.out.print(" val = "); for (int mm = 1; mm <= V; mm++) System.out.print(val[mm] + " "); System.out.println(); System.out.print("intree = "); for (int mm = 1; mm <= V; mm++) System.out.print(intree[mm] + " "); System.out.println(); System.out.println("k (next vertex added) = " + k); } intree[k] = true; if (DEBUG == 1) System.out.println("Updating neighbors of " + k); for (t = 1; t <= V; t++) { if (!intree[t]) // only update if not yet in "tree" { int prio = -flow[k][t]; // calculate priority if (size[k][t] > 0) prio += size[k][t]; if (prio > val[k]) prio = val[k]; // flow along a link // cannot be greater than the flow along the // previous link in the path if (prio > val[t]) // if greater than old { // update to new and val[t] = prio; // change dad dad[t] = k; if (DEBUG == 1) { System.out.print(" val[" + t + "] = " + val[t] + " "); System.out.println(" dad[" + t + "] = " + k); } } if (val[t] > val[max]) // keep track of biggest { max = t; if (DEBUG == 1) System.out.println(" New max is now " + max); } } } if (max == sink) return true; // if biggest is sink, we are done, since // that is the vertext we are trying to reach } return false; // return false to indicate that sink was not reached } // This constructor is implementing the main Ford and Fulkerson algorithm. // At each iteration an augmenting path is found (if it exists) and its // weight is added to the total. Then the residual graph is updated. public Flow() throws IOException { getData(); if (V <= 10) DEBUG = 1; // don't show debug stuff if V > 10 //showmat(); int totalflow = 0; int numaugments = 0; for (;;) { if (!bestaugpath(source, sink)) break; // if no aug. path found, stop int y = sink; int x = dad[sink]; totalflow += val[sink]; numaugments++; while (x != 0) // update residual graph by going { // backward along augmenting path flow[x][y] = flow[x][y] + val[sink]; // from sink to source flow[y][x] = -flow[x][y]; y = x; x = dad[y]; } if (DEBUG == 1) { System.out.println("Flow augmented by " + val[sink]); System.out.print("Path (starting at sink) = "); for (int i = sink; i != 0; i = dad[i]) System.out.print(i + ", "); System.out.println(); showmat(); System.out.println(); } } System.out.println("\nAfter " + numaugments + " augmentations, the " + "Max Flow result is: " + totalflow); //showmat(); } public static void main(String [] args) throws IOException { new Flow(); } }