week3的作业太难了,已经花了好长时间了(读题目、理解题目,算上零零碎碎的时间,都好几天了 - -)。还是先写下blog总结归纳下这周的工作吧,也为以后回顾方便些。
csdn大姨妈了!!!!先是把我写了一晚上加一上午的blog整得成类似源码的形式了,图片什么的都没有了,只得重新写啊!接着,写blog的链接怎么也访问不了,靠!!
1. 最小割问题
对于一个带权重的有向图中(权值这里称为capacity),图中包含一个源节点s和一个目标节点t,如下图所示:
定义:
(1)源节点s和目标节点t的一个切割就是将该有向图中的节点进行划分,使s在集合A中、t在集合B中。
(2)该切割的capacity即由集合A指向集合B的所有边的capacity(由B指向A的边不计算在内)。
最小割的问题就是找到这样的划分使得切割的capacity最小化。上图的切割可以如下(最后一个为最小割):
O.O
2. 最大流问题
与最小割问题的初始条件类似,每条边上增加了流的概念,问题的描述就更加顾名思义了:
3. Ford-Fulkerson algorithm
Ford-Fulkerson algorithm是解决最大流问题的一种算法,算法描述如下;
Start with 0 flow
While there exists an augmenting path:
- find an augmenting path
- compute bottleneck capacity
- increase flow on that path bu bottleneck capacity
详细描述如下的几个阶段:
step1:初始化流网络,置flow=0。
step2:在流网络中寻找由源节点s到目标节点t的一条路径,当然包括普通的路径和增广路径,是一个迭代的过程。总体看来,路径中包含这样的边:未满(flow未达到capacity)的forward边和非空的backword边。
@-@
@-@
@-@
step3:无法再找到从s到t的路径(forward边都满,backward边都空),结束迭代。
所有的问题都指向如何寻找增广路径:BFS可以轻松解决这个问题。
另外,还有几个有用的结论:
(1)最大流等于最小割容量。
(2)流达到最大流的条件当且仅当无增广路径。
(3)由最大流计算最小割(A, B):同样BFS解决。首先,带权有向图中再找不到增广路径;那么集合A就是从源节点s出发,依次经过非满的forward边或者非空的backward边访问节点,直至闭合。
4. Ford-Fulkerson algorithm 实现
4.1 残余网络
对于已经有网络流的图,一条边上的流f,容量c,则该条边上的残余量为c-f。增广路径是建立在残余网络基础上的,可以对残余网络有如下的构想:
4.2 数据结构
引入新的数据结构,包括边和图,但都是构建在之前的提出的边和图的数据结构之上的。边的数据结构包括了流和容量的概念,图的数据结构与有向图略有不同。具体还是看实现吧。
public class FlowEdge {
private final int v, w; // from and to
private final int double capacity; // capcacity
private double flow; // flow, flow variable
public FlowEdge(int v, int w, double capacity) {
this.v = v;
this.w = w;
this.capacity = capacity;
}
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("Illegal endpoint");
}
public double residualCapacityTo(int vertex) {
if (vertex == v) return flow; // backward edge
else if (vertex == w) return capacity-flow; // forward edge
else throw new IllegalArgumentException();
}
public void addResidualFlowTo(int vertex, double delta) {
if (vertex == v) flow -= delta; // backward
else if (vertex == w) flow += delta; // forward
else throw new IllegalArgumentException();
}
}
public class FlowNetwork {
private final int V;
private Bag<FlowEdge>[] adj;
public FlowNetwork(int V) {
this.V = V;
adj = (Bag<FlowEdge>[]) new Bag[V];
for (int v = 0; v < V; v++)
adj[v] = new Bag<FlowEdge>();
}
public void addEdge(FlowEdge e) {
int v = e.from();
int w = e.to();
adj[v].add(e);
adj[w].add(e);
}
public Iterable<FlowEdge> adj(int v) {
return adj[v];
}
}
4.3 算法实现
根据伪代码给出的算法思想,每次迭代找一条由s到t的路径,找到这条路径上可增量的最大值,依次对该条路径上的每条边相应增量(forward边值增加,backward边值减少)。而找一条s到t的路径通过BFS实现,从源节点s开始BFS看能否到达目标节点t,满足BFS的条件是经过的该条边forward边未满或backward边非空。
话不多说,贴代码:
public class FordFulkerson {
private boolean[] marked; // true if s->v path in residual network
private FlowEdge[] edgeTo; // last edge on s->v path
private double value; // value of flow
public FordFulkerson(FlowEdge G, int s, int t) {
value = 0.0;
while (hasAugmentingPath(G, s, t)) {
double bottle = Double.POSITIVE_INFINITY;
// compute bottleneck capacity
for (int v = t; v != s; v = edgeTo[v].other(v))
bottle = Math.min(bottle, edgeTo[v].residualCapacity(v));
// augment flow
for (int v = t; v != s; v = edgeTo[v].other(v))
edgeTo[v].addResidualFlowTo(v, bottle);
value += bottle;
}
}
public boolean hasAugmentingPath(FlowNetwork G, int s, int t) {
edgeTo = new FlowEdge[G.V()];
marked = new boolean[G.V()];
Queue<Integer> queue = new Queue<Integer>();
queue.enqueue(s);
marked[s] = true;
while (!queue.isEmpty()) {
int v = queue.dequeue();
for (FlowEdge e : G.adj(v)) {
int w = e.other(v);
// found path from s to w in the residual network
if (e.residualCapacity(w) > 0 && !marked[w]) {
edge[w] = e; // save last edge on path to w
marked[w] = true; // mark w
queue.enqueue(w); // add w to the queue
}
}
}
return marked[t]; // is t reachable from s in residual network
}
public double value() {
return value;
}
// is v reachable from s in residual network
public boolean inCut(int v) {
return marked[v];
}
}
不容易啊,顶着做不出week3作业的压力硬是把blog给写了(对于我这样一个有强迫症的人来说,不把一件事完成是绝不会去做另一件事的)。而且这篇blog花了一晚上加一整天啊,被坑坑的csdn折腾得不行了,这篇blog相当于是写了两遍了的,艰难不堪。
我会成功吗,我会在这一点点的积累中、一步一步的艰难前行中成就吗?我真的不知道,我希望现实能够像梦想一样美好,但是我不敢奢求,唯恐我承受的苦难还不够,还没到让我成功的时候。也许我会失望,不后悔吧。