算法34-最短增广路径的Ford-Fulkerson最大流量算法

读者盆友,下午好。这里介绍下最短增广路径的Ford-Fulkerson最大流量算法

看这个需要对“网络流量算法”中的基本概念有所了解,这里就不啰嗦了。
直奔主题哈。这是这段时间算法系列的最后一篇拉,也算是五一劳动节对自己的奖励吧。迟点去去吃好吃的:肠粉。

本博客代码示例均来自:算法 Algorithmes Forth Edition
[美] Robert Sedgewick Kevin Wayne 著 谢路云译

文章目录

  • 一、最短增广路径的Ford-Fulkerson最大流量算法
  • 二、数据类型:FlowEdge-流量网络中的边(剩余网络)
  • 三、数据类型-FlowNetWork
  • 四、源码:

一、最短增广路径的Ford-Fulkerson最大流量算法


package com.cmh.algorithm.background;

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.StdOut;

/**
 * 最短增广路径的Ford-Fulkerson最大流量算法
 * Author:起舞的日子
 * Date:  2020/5/1 17:40
 */
public class FordFulkerson {

    /**
     * 网络中的初始流量为零,沿着任意从起点到终点(且不含有饱和的正向边或是空逆向边)的增广路径增大流量
     * ,知道网络中不存在这样的路径为止
     */

    /**
     * 在剩余网络中是否存在从s到v的路径?
     */
    private boolean[] marked;

    /**
     * 从s到v的最短路径上的最后一条边
     */
    private FlowEdge[] edgeTo;

    /**
     * 当前最大流量
     */
    private double value;

    public FordFulkerson(FlowNetWork G, int s, int t) {
        //找出从s到t的流量网络G的最大流量配置
        while (hasAugmentingPath(G, s, t)) {
            //利用所有存在的增广路径
            //计算当前的瓶颈容量
            double bottle = Double.POSITIVE_INFINITY;
            for (int v = t; v != s; v = edgeTo[v].other(v)) {
                bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
            }
            //增大流量
            for (int v = t; v != s; v = edgeTo[v].other(v)) {
                edgeTo[v].addResidualFlowTo(v, bottle);
            }
            value += bottle;
        }
    }

    public double value() {
        return value;
    }

    public boolean inCut(int v) {
        return marked[v];
    }

    /**
     * 在剩余网络中屠龙过广度优先搜索寻找增广路径
     */
    private boolean hasAugmentingPath(FlowNetWork G, int s, int t) {

        //标记路径已知的顶点
        marked = new boolean[G.V()];

        //路径上最后一条边
        edgeTo = new FlowEdge[G.V()];
        Queue<Integer> q = new Queue<>();
        //标记起点
        marked[s] = true;
        q.enqueue(s);
        while (!q.isEmpty()) {
            int v = q.dequeue();
            for (FlowEdge e : G.adj(v)) {
                int w = e.other(v);
                if (e.residualCapacityTo(w) > 0 && !marked[w]) {
                    //在剩余网络中,对于任意一条连接到一个未被标记的顶点的边
                    //保存路径上最后一条边
                    edgeTo[w] = e;
                    //标记w,因为路径现在是已知的了
                    marked[w] = true;
                    q.enqueue(w);
                }
            }
        }

        return marked[t];
    }

    public static void main(String[] args) {
        FlowNetWork G = new FlowNetWork(new In(args[0]));
        int s = 0, t = G.V() - 1;
        FordFulkerson maxFlow = new FordFulkerson(G, s, t);
        StdOut.println("Max flow from " + s + " to" + t);
        for (int v = 0; v < G.V(); v++) {
            for (FlowEdge e : G.adj(v)) {
                if ((v == e.from()) && e.flow() > 0) {
                    StdOut.println("  " + e);
                }
            }
        }
        StdOut.println("Max flow value = " + maxFlow.value());

    }

}

下面是该算法需要用到的数据类型:FlowEdge 、FlowNetWork

二、数据类型:FlowEdge-流量网络中的边(剩余网络)

package com.cmh.algorithm.background;

/**
 * 流量网络中的边(剩余网络)
 * Author:起舞的日子
 * Date:  2020/5/1 17:45
 */
public class FlowEdge {
    /**
     * 边的起点
     */
    private final int v;

    /**
     * 边的终点
     */
    private final int w;

    /**
     * 容量
     */
    private final double capacity;

    /**
     * 流量
     */
    private double flow;

    public FlowEdge(int v, int w, double capacity) {
        this.v = v;
        this.w = w;
        this.capacity = capacity;
        this.flow = 0.0;
    }

    public int from() {
        return v;
    }

    public int to() {
        return w;
    }

    public double capacity() {
        return capacity;
    }

    public double flow() {
        return flow;
    }

    public int other(int vertex) {
        if (vertex == v) {
            return w;
        } else if (vertex == w) {
            return v;
        } else {
            throw new RuntimeException("Inconsistent edge");
        }
    }

    public double residualCapacityTo(int vertex) {
        if (vertex == v) {
            return flow;
        } else if (vertex == w) {
            return capacity - flow;
        } else {
            throw new RuntimeException(" Inconsistent edge");
        }
    }

    public void addResidualFlowTo(int vertex, double delta) {
        if (vertex == v) {
            flow = flow -= delta;
        } else if (vertex == w) {
            flow += delta;
        } else {
            throw new RuntimeException("Inconsistent edge");
        }
    }

    public String toString() {
        return String.format("%d->%d %2f %2.f", v, w, capacity, flow);
    }

}

三、数据类型-FlowNetWork

package com.cmh.algorithm.background;

import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

/**
 * Author:起舞的日子
 * Date:  2020/5/1 17:57
 */
public class FlowNetWork {

    private static final String NEWLINE = System.getProperty("line.separator");

    private final int V;

    private int E;

    private Bag<FlowEdge>[] adj;

    public FlowNetWork(int V) {
        if (V < 0) {
            throw new IllegalArgumentException("Number of vertices in a Graph must be nonnegative");
        }
        this.V = V;
        this.E = 0;
        adj = new Bag[V];
        for (int v = 0; v < V; v++) {
            adj[v] = new Bag<>();
        }
    }

    public FlowNetWork(int V, int E) {
        this(V);
        if (E < 0) {
            throw new IllegalArgumentException("Number of edges must be nonnegative");
        }
        for (int i = 0; i < E; i++) {
            int v = StdRandom.uniform(V);
            int w = StdRandom.uniform(V);
            double capacity = StdRandom.uniform(100);
            addEdge(new FlowEdge(v, w, capacity));
        }
    }

    public FlowNetWork(In in) {
        this(in.readInt());
        int E = in.readInt();
        if (E < 0) {
            throw new IllegalArgumentException("number of edges must be nonnegative");
        }
        for (int i = 0; i < E; i++) {
            int v = in.readInt();
            int w = in.readInt();
            validateVertex(v);
            validateVertex(w);
            double capacity = in.readDouble();
            addEdge(new FlowEdge(v, w, capacity));
        }
    }

    public void addEdge(FlowEdge e) {
        int v = e.from();
        int w = e.to();
        validateVertex(v);
        validateVertex(w);
        adj[v].add(e);
        adj[w].add(e);
        E++;
    }

    private void validateVertex(int v) {
        if (v < 0 || v >= V) {
            throw new IllegalArgumentException("vertex" + v + "is not between 0 and " + (V - 1));
        }
    }

    public int V() {
        return V;
    }

    public int E() {
        return E;
    }

    public Iterable<FlowEdge> adj(int v) {
        validateVertex(v);
        return adj(v);
    }

    public Iterable<FlowEdge> edges() {
        Bag<FlowEdge> list = new Bag<>();
        for (int v = 0; v < V; v++) {
            for (FlowEdge e : adj(v)) {
                if (e.to() != v) {
                    list.add(e);
                }
            }
        }
        return list;
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(V + " " + E + NEWLINE);
        for (int v = 0; v < V; v++) {
            s.append(v + ": ");
            for (FlowEdge e : adj(v)) {
                if (e.to() != v) {
                    s.append(e + " ");
                }
            }
            s.append(NEWLINE);
        }
        return s.toString();
    }

    public static void main(String[] args) {
        In in = new In(args[0]);
        FlowNetWork G = new FlowNetWork(in);
        StdOut.println(G);
    }

}

四、源码:

https://github.com/cmhhcm/my2020.git

好了,读者盆友,晚安。接下来我们将走入:设计模式系列。
然后是微服务系列:SpringBoot SpingCluod等。我们会争取在接下来的2周内完成。

你可能感兴趣的:(数据结构与算法)