《算法》第六章部分程序 part 5
▶ 书中第六章部分程序,包括在加上自己补充的代码,网络最大流 Ford - Fulkerson 算法,以及用到的流量边类和剩余流量网络类
● 网络最大流 Ford - Fulkerson 算法
package package01; import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.FlowEdge;
import edu.princeton.cs.algs4.FlowNetwork;
import edu.princeton.cs.algs4.Queue; public class class01
{
private static final double FLOATING_POINT_EPSILON = 1E-11; private final int V; // 顶点数
private boolean[] marked; // 已搜索顶点,marked[v] = true 表示从起点 s 到顶点 v 存在一条具有剩余流量的路径
private FlowEdge[] edgeTo; // s -> v 路径上的最后一条边
private double value; // 当前最大流量 public class01(FlowNetwork G, int s, int t)
{
V = G.V();
if (s == t || !isFeasible(G, s, t))
throw new IllegalArgumentException("\n<constructor> Source == Sink || !isFeasible(G, s, t).\n");
for (value = excess(G, t); hasAugmentingPath(G, s, t);) // 初始流量为顶点 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) // 顶点 v 是否在 s-切分中
{
return marked[v];
} private boolean hasAugmentingPath(FlowNetwork G, int s, int t) // 判断是否存在从 s 到 t 的剩余容量路径
{
edgeTo = new FlowEdge[G.V()];
marked = new boolean[G.V()];
Queue<Integer> queue = new Queue<Integer>(); // 广度优先搜索,保持所有搜索过的路径都有剩余容量
for (queue.enqueue(s), marked[s] = true; !queue.isEmpty() && !marked[t];)
{
int v = queue.dequeue();
for (FlowEdge e : G.adj(v))
{
int w = e.other(v);
if (e.residualCapacityTo(w) > 0) // 如果取出的边关于终点 w 有剩余流量,则将其入队
{
if (!marked[w])
{
edgeTo[w] = e;
marked[w] = true;
queue.enqueue(w);
}
}
}
}
return marked[t]; // 如果搜索到过 t,说明存在一条具有剩余容量的路径
} private double excess(FlowNetwork G, int v) // 计算顶点 v 的净流量
{
double excess = 0.0;
for (FlowEdge e : G.adj(v)) // 入边增加,出边减少
excess += (v == e.to() ? e.flow() : -e.flow());
return excess;
} private boolean isFeasible(FlowNetwork G, int s, int t) // 节点流量异常检查
{
for (int v = 0; v < G.V(); v++)
{
for (FlowEdge e : G.adj(v))
{
if (e.flow() < -FLOATING_POINT_EPSILON || e.flow() > e.capacity() + FLOATING_POINT_EPSILON) // 某条边的流量异常
{
System.err.println("Edge does not satisfy capacity constraints: " + e);
return false;
}
}
}
if (Math.abs(value + excess(G, s)) > FLOATING_POINT_EPSILON)// 当前起点净流量(<0)加上图流量非零,异常
{
System.err.println("Excess at source = " + excess(G, s) + "Max flow = " + value);
return false;
}
if (Math.abs(value - excess(G, t)) > FLOATING_POINT_EPSILON)// 当前终点净流量(>0)减去图流量非零,异常
{
System.err.println("Excess at sink = " + excess(G, t) + "Max flow = " + value);
return false;
}
for (int v = 0; v < G.V(); v++) // 其他节点净流量应该为零
{
if (v == s || v == t)
continue;
if (Math.abs(excess(G, v)) > FLOATING_POINT_EPSILON)
{
System.err.println("Net flow out of " + v + " doesn't equal zero");
return false;
}
}
return true;
} public static void main(String[] args)
{
int V = Integer.parseInt(args[0]), E = Integer.parseInt(args[1]), s = 0, t = V - 1;
FlowNetwork G = new FlowNetwork(V, E);
StdOut.println(G);
class01 maxflow = new class01(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.print("Min cut: "); // 输出最小切分
for (int v = 0; v < G.V(); v++)
if (maxflow.inCut(v)) StdOut.print(v + " ");
StdOut.println("\nMax flow value = " + maxflow.value());
}
}
● 流量边类
package package01; public class class01
{
private static final double FLOATING_POINT_EPSILON = 1E-10; private final int v; // 起点
private final int w; // 终点
private final double capacity; // 容量
private double flow; // 流量 public class01(int inputV, int inputW, double inputCapacity) // 三种构造函数
{
if (inputV < 0 || inputW < 0 || inputCapacity<0)
throw new IllegalArgumentException("\n<constructor> inputV < 0 || inputW < 0 || inputCapacity < 0.\n");
v = inputV;
w = inputW;
capacity = inputCapacity;
flow = 0.0;
} public class01(int inputV, int inputW, double inputCapacity, double inputFlow)
{
if (inputV < 0 || inputW < 0 || inputCapacity<0 || inputFlow < 0.0 || inputFlow > inputCapacity)
throw new IllegalArgumentException("\n<constructor> inputV < 0 || inputW < 0 || inputCapacity < 0 || flow < 0.0 || flow > capacity.\n");
v = inputV;
w = inputW;
capacity = inputCapacity;
flow = inputFlow;
} public class01(class01 e)
{
v = e.v;
w = e.w;
capacity = e.capacity;
flow = e.flow;
} 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;
throw new IllegalArgumentException("\n<other> invalid endpoint.\n");
} public double residualCapacityTo(int vertex) // 计算一条边关于某个顶点的流通能力
{
if (vertex == v) // 起点,返回边的现有流量
return flow;
else if (vertex == w) // 终点,返回边的剩余容量
return capacity - flow;
throw new IllegalArgumentException("\n<residualCapacityTo> invalid endpoint.\n");
} public void addResidualFlowTo(int vertex, double delta) // 改变边的流量
{
if (delta < 0.0)
throw new IllegalArgumentException("\n<addResidualFlowTo> delta < 0.0.\n");
if (vertex == v) // 起点,边流量减少
flow -= delta;
else if (vertex == w) // 终点,边流量增加
flow += delta;
else
throw new IllegalArgumentException("\n<addResidualFlowTo> invalid endpoint.\n");
if (Math.abs(flow) <= FLOATING_POINT_EPSILON) // 流量归零
flow = 0;
if (Math.abs(flow - capacity) <= FLOATING_POINT_EPSILON) // 流量达到容量
flow = capacity;
if (flow < 0.0) // 流量异常
throw new IllegalArgumentException("\n<addResidualFlowTo> flow <0.\n");
if (flow > capacity)
throw new IllegalArgumentException("\n<addResidualFlowTo> flow > capacity.\n");
} public String toString()
{
return v + "->" + w + " " + flow + "/" + capacity;
} public static void main(String[] args)
{
class01 e = new class01(12, 23, 4.56);
StdOut.println(e);
}
}
● 剩余流量网络类
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.FlowEdge;
import edu.princeton.cs.algs4.Bag; public class class01
{
private static final String NEWLINE = System.getProperty("line.separator"); private final int V;
private int E;
private Bag<FlowEdge>[] adj; public class01(int inputV)
{
if (V < 0)
throw new IllegalArgumentException("\n<constructor> inputV < 0.\n");
V = inputV;
E = 0;
adj = (Bag<FlowEdge>[]) new Bag[V];
for (int v = 0; v < V; v++)
adj[v] = new Bag<FlowEdge>();
} public class01(int inputV, int inputE)
{
this(inputV);
if (E < 0)
throw new IllegalArgumentException("\n<constructor> inputE < 0.\n");
for (int i = 0; i < E; i++)
addEdge(new FlowEdge(StdRandom.uniform(V), StdRandom.uniform(V), StdRandom.uniform(100)));
} public class01(In in)
{
this(in.readInt());
E = in.readInt();
for (int i = 0; i < E; i++)
addEdge(new FlowEdge(in.readInt(), in.readInt(), in.readDouble()));
} public int V()
{
return V;
} public int E()
{
return E;
} public void addEdge(FlowEdge e) // 添加一条边
{
adj[e.from()].add(e);
adj[e.to()].add(e);
E++;
} public Iterable<FlowEdge> adj(int v)// 一个顶点上出边的迭代器,就是该顶点的单链表
{
return adj[v];
} public Iterable<FlowEdge> edges() // 所有边的迭代器,和搜集所有顶点处的出边
{
Bag<FlowEdge> list = new Bag<FlowEdge>();
for (int v = 0; v < V; v++)
{
for (FlowEdge e : adj(v))
{
if (e.to() != v)
list.add(e);
}
}
return list;
} 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]);
class01 G = new class01(in);
StdOut.println(G);
}
}
《算法》第六章部分程序 part 5的更多相关文章
- 《算法》第六章部分程序 part 7
▶ 书中第六章部分程序,加上自己补充的代码,包括全局最小切分 Stoer-Wagner 算法,最小权值二分图匹配 ● 全局最小切分 Stoer-Wagner 算法 package package01; ...
- 《算法》第六章部分程序 part 6
▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法 ● 二分图最大匹配(最小顶点覆盖)的交替路径算法 package ...
- 《算法》第六章部分程序 part 8
▶ 书中第六章部分程序,加上自己补充的代码,包括单纯形法求解线性规划问题 ● 单纯形法求解线性规划问题 // 表上作业法,I 为单位阵,y 为对偶变量,z 为目标函数值 // n m 1 // ┌── ...
- 《算法》第六章部分程序 part 4
▶ 书中第六章部分程序,包括在加上自己补充的代码,利用后缀树查找最长重复子串.查找最大重复子串并输出其上下文(Key word in context,KWIC).求两字符串的最长公共子串 ● 利用后缀 ...
- 《算法》第六章部分程序 part 3
▶ 书中第六章部分程序,包括在加上自己补充的代码,后缀树的两种实现 ● 后缀树实现一 package package01; import java.util.Arrays; import edu.pr ...
- 《算法》第六章部分程序 part 2
▶ 书中第六章部分程序,包括在加上自己补充的代码,B-树 ● B-树 package package01; import edu.princeton.cs.algs4.StdOut; public c ...
- 《算法》第六章部分程序 part 1
▶ 书中第六章部分程序,包括在加上自己补充的代码,粒子碰撞系统及用到的粒子类 ● 粒子系统 package package01; import java.awt.Color; import edu.p ...
- 《算法》第一章部分程序 part 1
▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...
- 《算法》第二章部分程序 part 5
▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...
随机推荐
- ELK Stack 笔记
ELK Stack ELK Stack ELK Stack ELK 介绍 架构 Elasticsearch 安装 常见问题 关闭 Elasticsearch Elasticsearch-head Ki ...
- VS2012打开项目——已停止工作
VS2012打开项目——已停止工作 解决方法如下: 1. 开始-->所有程序-->Microsoft Visual Studio 2012-->Visual Studio Tools ...
- DS哈希查找--线性探测再散列
题目描述 定义哈希函数为H(key) = key%11.输入表长(大于.等于11),输入关键字集合,用线性探测再散列构建哈希表,并查找给定关键字. --程序要求-- 若使用C++只能include一个 ...
- 【linux】之Centos6.x升级glibc
因为Centos比较保守依赖的glibc最高版本是2.12 rpm -qa|grep glibc strings /lib64/libc.so. |grep GLIBC_ 但是经常我们安装一些源码包, ...
- C++进阶--解谜operator new/delete
//############################################################################ // 解谜operator new/del ...
- elasticsearch 5.1 别的机器无法访问9200端口
版权声明:作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文版权归作者和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显 ...
- Java学习——Applet菜单
程序功能:在窗口中添加菜单栏,在菜单栏添加菜单项,并添加下拉菜单和 2 级菜单,通过选择菜单项可以执行不同操作,生成如下图所示窗口. package cys; import java.awt.*; i ...
- JSON 使用
ylbtech-JSON: JSON 使用 1. 把 JSON 文本转换为 JavaScript 对象返回顶部 JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为 ...
- 基于JavaScript 声明全局变量的三种方式详解
原文地址:http://www.jb51.net/article/36548.htm JS中声明全局变量主要分为显式声明或者隐式声明下面分别介绍. 声明方式一: 使用var(关键字)+变量名(标识符) ...
- go中的make和new的区别
适用范围:make 只能创建内建类型(slice map channel), new 则是可以对所有类型进行内存分配 返回值: new 返回指针, make 返回引用 填充值: new 填充零值, m ...