《算法》第六章部分程序 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 ...
随机推荐
- 跟着未名学 - 免费录屏工具ActivePresenter
目录 简介... 1 录制屏幕... 1 编辑... 3 幻灯片... 3 时间轴... 4 对像... 4 导出视频... 4 未名 2017-7-17 简介 ActivePresenter 个人免 ...
- php访问SQLserver时加载的dll
php_sqlsrv_55_ts.dll 线程t安全sphp_sqlsrv_55_nts.dll 非n线程t安全s
- centos 磁盘清理 /dev/vda1系统盘满了
df -h 检查一台服务器磁盘使用空间,发现磁盘已经使用了100% 思路是: 1.cd /usr 当然这里不一定是/usr目录,最好是cd到 根目录再执行下一步 2.du -sh * 看哪 ...
- Android开发之五大布局篇
一.Android中常用的5大布局方式有以下几种: 线性布局(LinearLayout):按照垂直或者水平方向布局的组件. 相对布局(RelativeLayout):相对其它组件的布局方式. 绝对布局 ...
- uoj#274. 【清华集训2016】温暖会指引我们前行
http://uoj.ac/problem/274 由于边权互不相同,只需用lct维护带加边的最大生成树 #include<bits/stdc++.h> #define lc ch][0 ...
- 使用python操作word
有两种方式: 使用win32com 使用docx 1.使用win32com扩展包 只对windows平台有效 代码: # coding=utf-8 import win32com from win32 ...
- java浅析final关键字
谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...
- P2430严酷的训练
传送 这个题的题干很长,长到令人恶心 这个题的p乍一看好像没有卵用,但其实他很有用(废话).这里的“费用”不再是tw[i](wky做第i道题的时间),而是tw[p[i]](wky做第i道题所对应的知识 ...
- Java-Runoob-高级教程-实例-方法:05. Java 实例 – 阶乘
ylbtech-Java-Runoob-高级教程-实例-方法:05. Java 实例 – 阶乘 1.返回顶部 1. Java 实例 - 阶乘 Java 实例 一个正整数的阶乘(英语:factoria ...
- 【转】XP_cmdshell存储过程
原文地址:http://www.cnblogs.com/love_study/archive/2010/03/02/1676583.html 一 .简介 xp_cmdshell 扩展存储过程将命令字符 ...