基础理论
剩余容量residual capicity的概念比较简单,就是容量减去流量,设起点为
u
u
u,终点为
v
v
v,公式为:
c
f
(
u
,
v
)
=
c
(
u
,
v
)
−
f
(
u
,
v
)
c_f(u,v)=c(u,v)-f(u,v)
cf(u,v)=c(u,v)−f(u,v)
不饱和边集unsaturated edges,是在特定的流量模型
f
f
f下所有剩余容量大于0的边的集合,数学符号为
E
f
E_f
Ef。
剩余网络redisual network,是在特定的流量模型f下,所有的点和不饱和边组成的网络。剩余网络,肯定是原网络的子图,也因为点集相等,所以也是原网络的伸展子图。
增广路径augmenting path,是特定流量模型
f
f
f下剩余网络中从起点s到终点f的路径path。
增广路径定理Augmenting Path Theorem,说的是一个流
f
f
f如果是最大流当且仅当剩余网络中不存在增广路径。
算法及例子
这个算法超级简单啊,就是利用增广路径定理。首先是初始化流为0,这是句废话了,初始化都应该是0。
然后是从剩余网络里,找到一条增广路径,再取出增广路径里最小的剩余容量。然后把增广路径里的流全部加上最小剩余容量,再去找增广路径,直到找不出增广路径。根据增广路径定理,找不出的时候,就是最大流了。
如果每次都找的是任意路径,那就是Ford-Fulkerson算法。如果每次找到的增广路径都是最短路径,那就是Edmonds-Karp算法。
我以那个被无数大学和结构使用的流量图为例子,使用Edmonds-Karp算法:
第一次循环,找到了一条路径:
第二次循环,先把流量加上,再找增广路径:
第三次循环:
第四次循环:
第五次循环:
最终找不到增广路径,跳出循环,达到最大流:
Python代码
# _*_ coding:utf-8 _*_
class Edge:
def __init__(self, to, weight):
self.__to = to
self.__weight = weight
@property
def to(self):
return self.__to
@to.setter
def to(self, to):
self.__to = to
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, weight):
self.__weight = weight
# 加权图
class FlowNetwork:
def __init__(self, vertices, edges, s, t):
self.__vertices = vertices
self.__edges = edges
self.__s = s
self.__t = t
def ford_fulkerson(self):
# init
flow = [[Edge(x.to, 0) for x in e] for e in self.__edges]
while (p := self.augmenting_path(flow)) is not None:
# 找出最小的剩余容量
cf_min = p[1]
path = p[0]
# path上所有流量加上最小剩余流量
for i in range(len(path) - 1):
flows = flow[path[i]]
for x in flows:
if x.to == path[i + 1]:
x.weight += cf_min
return flow
WHITE = 0
GRAY = 1
BLACK = 2
def augmenting_path(self, flow):
# 使用单源bfs找到路径
parent_map = [None for _ in self.__vertices]
# 因为python 没有hashset,所以以字典代替
colors = [FlowNetwork.WHITE for _ in self.__vertices]
colors[self.__s] = FlowNetwork.GRAY
queue = [self.__s]
e = self.__s
while e != self.__t and len(queue) != 0:
e = queue.pop(0)
# 把点连接的点全部放入队列中
for neighbor in self.__edges[e]:
f = FlowNetwork.get_flow(flow, e, neighbor.to)
cf = neighbor.weight - f
if cf > 0:
if colors[neighbor.to] == FlowNetwork.WHITE:
# 必须是不饱和边
parent_map[neighbor.to] = e
queue.append(neighbor.to)
colors[neighbor.to] = FlowNetwork.GRAY
colors[e] = FlowNetwork.BLACK
# 没有找到
if e != self.__t:
return None
# 找到之后组装路径
path = [self.__t]
end = self.__t
min_cf = None
while end != self.__s:
# 不停地找parent
c = FlowNetwork.get_flow(self.__edges, parent_map[end], end)
f = FlowNetwork.get_flow(flow, parent_map[end], end)
cf = c - f
min_cf = cf if min_cf is None else min(min_cf, cf)
end = parent_map[end]
path.append(end)
path.reverse()
return path, min_cf
@staticmethod
def get_flow(flow, source, target):
flows = flow[source]
f = None
for edge_flow in flows:
if edge_flow.to == target:
f = edge_flow.weight
return f
def to_dot(self, pos):
dot_s = 'digraph s {\n\tlayout=fdp\n'
for i, v in enumerate(self.__vertices):
dot_s += f'\t"{v}"[pos="{pos[i]}"];\n'
for i, e in enumerate(self.__edges):
for t in e:
dot_s += f'\t\"{self.__vertices[i]}\"->"{self.__vertices[t.to]}"[label="{t.weight}"];\n'
dot_s += '}\n'
return dot_s
def to_flow_dot(self, pos, flow):
dot_s = 'digraph s {\n\tlayout=fdp\nnode [shape = circle]\n'
for i, v in enumerate(self.__vertices):
dot_s += f'\t"{v}"[pos="{pos[i]}"];\n'
for i, e in enumerate(self.__edges):
for t in e:
edge = f'\t\"{self.__vertices[i]}\"->"{self.__vertices[t.to]}"'
dot_s += edge
f = FlowNetwork.get_flow(flow, i, t.to)
dot_s += f'[label="{f}/{t.weight}"];\n'
dot_s += '}\n'
return dot_s
def to_flow_path_dot(self, pos, flow, path):
path_map = {}
for i in range(len(path) - 1):
path_map[path[i]] = path[i + 1]
dot_s = 'digraph s {\n\tlayout=fdp\nnode [shape = circle]\n'
for i, v in enumerate(self.__vertices):
dot_s += f'\t"{v}"[pos="{pos[i]}"];\n'
for i, e in enumerate(self.__edges):
for t in e:
edge = f'\t\"{self.__vertices[i]}\"->"{self.__vertices[t.to]}"'
dot_s += edge
f = FlowNetwork.get_flow(flow, i, t.to)
# 遇到路径就加粗
dot_s += f'[label="{f}/{t.weight}";'
if i in path_map and path_map[i] == t.to:
dot_s += 'color="red"'
dot_s += '];\n'
dot_s += '}\n'
return dot_s
@property
def vertices(self):
return self.__vertices
@vertices.setter
def vertices(self, value):
self.__vertices = value
@property
def edges(self):
return self.__edges
@edges.setter
def edges(self, value):
self.__edges = value
@property
def s(self):
return self.__s
@s.setter
def s(self, s):
self.__s = s
@property
def t(self):
return self.__t
@t.setter
def t(self, value):
self.__t = value
Python测试数据
import unittest
from com.youngthing.graph.ford_fulkerson import Edge
from com.youngthing.graph.ford_fulkerson import FlowNetwork
class ForkFulkersonTestCase(unittest.TestCase):
def test_fork_fulkerson(self):
# 做一张图
vertices = ['S', '2', '3', '4', '5', '6', '7', 'T']
# 设计容量
edges = [
[Edge(1, 10), Edge(2, 5), Edge(3, 15)],
[Edge(2, 4), Edge(4, 9), Edge(5, 15)],
[Edge(3, 4), Edge(5, 8)],
[Edge(6, 30)],
[Edge(5, 15), Edge(7, 10)],
[Edge(6, 15), Edge(7, 10)],
[Edge(7, 10), Edge(2, 6)],
[]
]
fn = FlowNetwork(vertices, edges, 0, 7)
pos = ["0,0!", "2,1!", "2,0!", "2,-1!", "4,1!", "4,0!", "4,-1!", "6,0!"]
print(fn.to_dot(pos))
flow = fn.ford_fulkerson()
print(fn.to_flow_dot(pos, flow))
if __name__ == '__main__':
unittest.main()