Edmonds-Karp 算法: 使用bfs找增广路经,复杂度min(O(VE^2), O(EF)), 其中F是最大流的流量. 依据是
最大流最小割定理:
1. 对于任一流f和任一割c, 流量|f| = |c| <= c的容量。
2. f 是最大流 <==> 残留网络Gf没有增广路径 <==> 存在一个割,其容量等于f的流量。
应用例子
====
liquids in pipes, parts through assembly lines, current throw electrical networks.
1. 求一个无向图的边连通度, 意思是只有至少去掉k条边才能使得图不连通。例如树的边连通度为1,圈的为2。
答案就是最大流对应的最小割。
2. 二分图的最大匹配。
答案: 设图顶点分成L和F两部分,L 到F 的边容量是1, 增加两个顶点s到L, R到t,
容量为无穷大。
3. 最少路径覆盖所有顶点。minimum path cover.
语言注记
====
1. 如果Type是非基本类型,则Type[][] t = new Type[10][2] 实际相当于void* t[10][2] 与Type本身没有关系;
因此对于每个t[i][j] 都要new, 这里的问题是,new次数多了内存碎片影响有多大?
如果容量是整数,直接把v,cap,flow,res 编码为t[4*i+0, ..., 4*i+3]好了。
2. 下面加了一个iterator对象,这样做的最大好处是让上层应用不依赖底层的表示;以后如果换成链表上层
最大流最小割定理:
1. 对于任一流f和任一割c, 流量|f| = |c| <= c的容量。
2. f 是最大流 <==> 残留网络Gf没有增广路径 <==> 存在一个割,其容量等于f的流量。
应用例子
====
liquids in pipes, parts through assembly lines, current throw electrical networks.
1. 求一个无向图的边连通度, 意思是只有至少去掉k条边才能使得图不连通。例如树的边连通度为1,圈的为2。
答案就是最大流对应的最小割。
2. 二分图的最大匹配。
答案: 设图顶点分成L和F两部分,L 到F 的边容量是1, 增加两个顶点s到L, R到t,
容量为无穷大。
3. 最少路径覆盖所有顶点。minimum path cover.
语言注记
====
1. 如果Type是非基本类型,则Type[][] t = new Type[10][2] 实际相当于void* t[10][2] 与Type本身没有关系;
因此对于每个t[i][j] 都要new, 这里的问题是,new次数多了内存碎片影响有多大?
如果容量是整数,直接把v,cap,flow,res 编码为t[4*i+0, ..., 4*i+3]好了。
2. 下面加了一个iterator对象,这样做的最大好处是让上层应用不依赖底层的表示;以后如果换成链表上层
什么都不用改。
代码:
/*Flow Network
*/
import java.util.*;
import java.io.*;
class Edge
{
int v; /*临接顶点*/
int cap; /*边的容量, -1表示非原始边*/
int flow; /*边上的流量*/
int res; /*边的残余容量*/
}
public class Flow{
int n; /*顶点数*/
Edge[][] edges; /*临接表*/
int[] degree; /*临接的边数*/
int[] parents; /*记录路径*/
int source, sink;
class EdgeIterator implements Iterator {
Edge[] e;
int deg;
int i;
public EdgeIterator(int v){
e = edges[v];
deg = degree[v];
i = 0;
}
public boolean hasNext(){
return i < deg;
}
public Edge next(){
return e[i++];
}
public void remove(){
System.out.println("not impl");
}
}
public Iterator iterator(int v){
return new EdgeIterator(v);
}
public Flow(int vertexNum, int edgeNum, Scanner in) throws IOException
{
int i, j;
n = vertexNum;
edges = new Edge[n][10];
for(i = 0; i < n; ++i){
for(j = 0; j < edges[i].length; ++j)
edges[i][j] = new Edge();
}
degree = new int[n];
parents = new int[n];
source = in.nextInt(); sink = in.nextInt();
while(edgeNum-- > 0){
int u, v, c;
u = in.nextInt(); v = in.nextInt(); c = in.nextInt();
addEdge(u, v, c);
}
}
void ensureCapacity(int v)
{
int i;
if(degree[v] >= edges[v].length){
edges[v] = Arrays.copyOf(edges[v], degree[v] + 10);
for(i = degree[v]; i < degree[v] + 10; ++i){
edges[v][i] = new Edge();
}
}
}
void addEdge(int i, int j, int capacity)
{/*增加边并同时构建残余边*/
Edge a, b;
int flow = 0; /*初始流量*/
ensureCapacity(i);
a = edges[i][degree[i]++];
a.v = j;
a.flow = flow;
a.res = capacity - a.flow;
a.cap = capacity;
ensureCapacity(j);
b = edges[j][degree[j]++];
b.v = i;
b.flow = flow;
b.res = b.flow;
b.cap = -1;
}
Edge findEdge(int x, int y)
{
int i;
for(i = 0; i < degree[x]; ++i)
if(edges[x][i].v == y)
return edges[x][i];
return null;
}
void dumpFlows()
{
int i, j;
Edge e;
System.out.printf(" f/c:%n");
for(i = 0; i < n; ++i){
Iterator iter = iterator(i);
while(iter.hasNext()){
e = iter.next();
//for(j = 0; j < degree[i]; ++j){
//e = edges[i][j];
if(e.cap > 0)System.out.printf("
本文介绍了Edmonds-Karp算法的原理及应用,该算法通过广度优先搜索寻找增广路径,实现最大流问题的高效解决。文中还探讨了算法的具体实现细节,并给出了几个典型的应用场景,包括液体管道流动、电路电流分析等。
6049

被折叠的 条评论
为什么被折叠?



