▶ 书中第六章部分程序,包括在加上自己补充的代码,网络最大流 Ford - Fulkerson 算法,以及用到的流量边类和剩余流量网络类
● 网络最大流 Ford - Fulkerson 算法
1 package package01; 2 3 import edu.princeton.cs.algs4.StdOut; 4 import edu.princeton.cs.algs4.FlowEdge; 5 import edu.princeton.cs.algs4.FlowNetwork; 6 import edu.princeton.cs.algs4.Queue; 7 8 public class class01 9 { 10 private static final double FLOATING_POINT_EPSILON = 1E-11; 11 12 private final int V; // 顶点数 13 private boolean[] marked; // 已搜索顶点,marked[v] = true 表示从起点 s 到顶点 v 存在一条具有剩余流量的路径 14 private FlowEdge[] edgeTo; // s -> v 路径上的最后一条边 15 private double value; // 当前最大流量 16 17 public class01(FlowNetwork G, int s, int t) 18 { 19 V = G.V(); 20 if (s == t || !isFeasible(G, s, t)) 21 throw new IllegalArgumentException(" <constructor> Source == Sink || !isFeasible(G, s, t). "); 22 for (value = excess(G, t); hasAugmentingPath(G, s, t);) // 初始流量为顶点 t 净流量,只要还存在具有剩余容量的路径就要扩容 23 { 24 double bottle = Double.POSITIVE_INFINITY; // 沿着找到的路径走一遍,最小剩余容量作为扩容量 25 for (int v = t; v != s; v = edgeTo[v].other(v)) 26 bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v)); 27 for (int v = t; v != s; v = edgeTo[v].other(v)) // 扩容 28 edgeTo[v].addResidualFlowTo(v, bottle); 29 value += bottle; // 总流量也要增加 30 } 31 } 32 33 public double value() 34 { 35 return value; 36 } 37 38 public boolean inCut(int v) // 顶点 v 是否在 s-切分中 39 { 40 return marked[v]; 41 } 42 43 private boolean hasAugmentingPath(FlowNetwork G, int s, int t) // 判断是否存在从 s 到 t 的剩余容量路径 44 { 45 edgeTo = new FlowEdge[G.V()]; 46 marked = new boolean[G.V()]; 47 Queue<Integer> queue = new Queue<Integer>(); // 广度优先搜索,保持所有搜索过的路径都有剩余容量 48 for (queue.enqueue(s), marked[s] = true; !queue.isEmpty() && !marked[t];) 49 { 50 int v = queue.dequeue(); 51 for (FlowEdge e : G.adj(v)) 52 { 53 int w = e.other(v); 54 if (e.residualCapacityTo(w) > 0) // 如果取出的边关于终点 w 有剩余流量,则将其入队 55 { 56 if (!marked[w]) 57 { 58 edgeTo[w] = e; 59 marked[w] = true; 60 queue.enqueue(w); 61 } 62 } 63 } 64 } 65 return marked[t]; // 如果搜索到过 t,说明存在一条具有剩余容量的路径 66 } 67 68 private double excess(FlowNetwork G, int v) // 计算顶点 v 的净流量 69 { 70 double excess = 0.0; 71 for (FlowEdge e : G.adj(v)) // 入边增加,出边减少 72 excess += (v == e.to() ? e.flow() : -e.flow()); 73 return excess; 74 } 75 76 private boolean isFeasible(FlowNetwork G, int s, int t) // 节点流量异常检查 77 { 78 for (int v = 0; v < G.V(); v++) 79 { 80 for (FlowEdge e : G.adj(v)) 81 { 82 if (e.flow() < -FLOATING_POINT_EPSILON || e.flow() > e.capacity() + FLOATING_POINT_EPSILON) // 某条边的流量异常 83 { 84 System.err.println("Edge does not satisfy capacity constraints: " + e); 85 return false; 86 } 87 } 88 } 89 if (Math.abs(value + excess(G, s)) > FLOATING_POINT_EPSILON)// 当前起点净流量(<0)加上图流量非零,异常 90 { 91 System.err.println("Excess at source = " + excess(G, s) + "Max flow = " + value); 92 return false; 93 } 94 if (Math.abs(value - excess(G, t)) > FLOATING_POINT_EPSILON)// 当前终点净流量(>0)减去图流量非零,异常 95 { 96 System.err.println("Excess at sink = " + excess(G, t) + "Max flow = " + value); 97 return false; 98 } 99 for (int v = 0; v < G.V(); v++) // 其他节点净流量应该为零 100 { 101 if (v == s || v == t) 102 continue; 103 if (Math.abs(excess(G, v)) > FLOATING_POINT_EPSILON) 104 { 105 System.err.println("Net flow out of " + v + " doesn't equal zero"); 106 return false; 107 } 108 } 109 return true; 110 } 111 112 public static void main(String[] args) 113 { 114 int V = Integer.parseInt(args[0]), E = Integer.parseInt(args[1]), s = 0, t = V - 1; 115 FlowNetwork G = new FlowNetwork(V, E); 116 StdOut.println(G); 117 class01 maxflow = new class01(G, s, t); 118 StdOut.println("Max flow from " + s + " to " + t); 119 for (int v = 0; v < G.V(); v++) // 打印所有具有流量的边 120 { 121 for (FlowEdge e : G.adj(v)) 122 { 123 if ((v == e.from()) && e.flow() > 0) 124 StdOut.println(" " + e); 125 } 126 } 127 StdOut.print("Min cut: "); // 输出最小切分 128 for (int v = 0; v < G.V(); v++) 129 if (maxflow.inCut(v)) StdOut.print(v + " "); 130 StdOut.println(" Max flow value = " + maxflow.value()); 131 } 132 }
● 流量边类
1 package package01; 2 3 public class class01 4 { 5 private static final double FLOATING_POINT_EPSILON = 1E-10; 6 7 private final int v; // 起点 8 private final int w; // 终点 9 private final double capacity; // 容量 10 private double flow; // 流量 11 12 public class01(int inputV, int inputW, double inputCapacity) // 三种构造函数 13 { 14 if (inputV < 0 || inputW < 0 || inputCapacity<0) 15 throw new IllegalArgumentException(" <constructor> inputV < 0 || inputW < 0 || inputCapacity < 0. "); 16 v = inputV; 17 w = inputW; 18 capacity = inputCapacity; 19 flow = 0.0; 20 } 21 22 public class01(int inputV, int inputW, double inputCapacity, double inputFlow) 23 { 24 if (inputV < 0 || inputW < 0 || inputCapacity<0 || inputFlow < 0.0 || inputFlow > inputCapacity) 25 throw new IllegalArgumentException(" <constructor> inputV < 0 || inputW < 0 || inputCapacity < 0 || flow < 0.0 || flow > capacity. "); 26 v = inputV; 27 w = inputW; 28 capacity = inputCapacity; 29 flow = inputFlow; 30 } 31 32 public class01(class01 e) 33 { 34 v = e.v; 35 w = e.w; 36 capacity = e.capacity; 37 flow = e.flow; 38 } 39 40 public int from() 41 { 42 return v; 43 } 44 45 public int to() 46 { 47 return w; 48 } 49 50 public double capacity() 51 { 52 return capacity; 53 } 54 55 public double flow() 56 { 57 return flow; 58 } 59 60 public int other(int vertex) 61 { 62 if (vertex == v) 63 return w; 64 else if (vertex == w) 65 return v; 66 throw new IllegalArgumentException(" <other> invalid endpoint. "); 67 } 68 69 public double residualCapacityTo(int vertex) // 计算一条边关于某个顶点的流通能力 70 { 71 if (vertex == v) // 起点,返回边的现有流量 72 return flow; 73 else if (vertex == w) // 终点,返回边的剩余容量 74 return capacity - flow; 75 throw new IllegalArgumentException(" <residualCapacityTo> invalid endpoint. "); 76 } 77 78 public void addResidualFlowTo(int vertex, double delta) // 改变边的流量 79 { 80 if (delta < 0.0) 81 throw new IllegalArgumentException(" <addResidualFlowTo> delta < 0.0. "); 82 if (vertex == v) // 起点,边流量减少 83 flow -= delta; 84 else if (vertex == w) // 终点,边流量增加 85 flow += delta; 86 else 87 throw new IllegalArgumentException(" <addResidualFlowTo> invalid endpoint. "); 88 if (Math.abs(flow) <= FLOATING_POINT_EPSILON) // 流量归零 89 flow = 0; 90 if (Math.abs(flow - capacity) <= FLOATING_POINT_EPSILON) // 流量达到容量 91 flow = capacity; 92 if (flow < 0.0) // 流量异常 93 throw new IllegalArgumentException(" <addResidualFlowTo> flow <0. "); 94 if (flow > capacity) 95 throw new IllegalArgumentException(" <addResidualFlowTo> flow > capacity. "); 96 } 97 98 public String toString() 99 { 100 return v + "->" + w + " " + flow + "/" + capacity; 101 } 102 103 public static void main(String[] args) 104 { 105 class01 e = new class01(12, 23, 4.56); 106 StdOut.println(e); 107 } 108 }
● 剩余流量网络类
1 package package01; 2 3 import edu.princeton.cs.algs4.In; 4 import edu.princeton.cs.algs4.StdOut; 5 import edu.princeton.cs.algs4.StdRandom; 6 import edu.princeton.cs.algs4.FlowEdge; 7 import edu.princeton.cs.algs4.Bag; 8 9 public class class01 10 { 11 private static final String NEWLINE = System.getProperty("line.separator"); 12 13 private final int V; 14 private int E; 15 private Bag<FlowEdge>[] adj; 16 17 public class01(int inputV) 18 { 19 if (V < 0) 20 throw new IllegalArgumentException(" <constructor> inputV < 0. "); 21 V = inputV; 22 E = 0; 23 adj = (Bag<FlowEdge>[]) new Bag[V]; 24 for (int v = 0; v < V; v++) 25 adj[v] = new Bag<FlowEdge>(); 26 } 27 28 public class01(int inputV, int inputE) 29 { 30 this(inputV); 31 if (E < 0) 32 throw new IllegalArgumentException(" <constructor> inputE < 0. "); 33 for (int i = 0; i < E; i++) 34 addEdge(new FlowEdge(StdRandom.uniform(V), StdRandom.uniform(V), StdRandom.uniform(100))); 35 } 36 37 public class01(In in) 38 { 39 this(in.readInt()); 40 E = in.readInt(); 41 for (int i = 0; i < E; i++) 42 addEdge(new FlowEdge(in.readInt(), in.readInt(), in.readDouble())); 43 } 44 45 public int V() 46 { 47 return V; 48 } 49 50 public int E() 51 { 52 return E; 53 } 54 55 public void addEdge(FlowEdge e) // 添加一条边 56 { 57 adj[e.from()].add(e); 58 adj[e.to()].add(e); 59 E++; 60 } 61 62 public Iterable<FlowEdge> adj(int v)// 一个顶点上出边的迭代器,就是该顶点的单链表 63 { 64 return adj[v]; 65 } 66 67 public Iterable<FlowEdge> edges() // 所有边的迭代器,和搜集所有顶点处的出边 68 { 69 Bag<FlowEdge> list = new Bag<FlowEdge>(); 70 for (int v = 0; v < V; v++) 71 { 72 for (FlowEdge e : adj(v)) 73 { 74 if (e.to() != v) 75 list.add(e); 76 } 77 } 78 return list; 79 } 80 81 public String toString() 82 { 83 StringBuilder s = new StringBuilder(); 84 s.append(V + " " + E + NEWLINE); 85 for (int v = 0; v < V; v++) 86 { 87 s.append(v + ": "); 88 for (FlowEdge e : adj[v]) 89 if (e.to() != v) s.append(e + " "); 90 s.append(NEWLINE); 91 } 92 return s.toString(); 93 } 94 95 public static void main(String[] args) 96 { 97 In in = new In(args[0]); 98 class01 G = new class01(in); 99 StdOut.println(G); 100 } 101 }