目录
概念介绍
流(Flow)
一种抽象的实体,在源点流出,通过边输送,在汇点被吸收,将目标从一个地点输送到另一个地点。
网络流(Network flow)
在一个每条边都有容量(Capacity)的有向图分配流,使一条边的流量不会超过它的容量。
流网络 ( G , s , t , c ) (G,s,t,c) (G,s,t,c)
(𝑮,𝒔,𝒕,𝒄)
𝑮→图(𝐺𝑟𝑜𝑢𝑝) 𝒔→源点(𝑆𝑜𝑢𝑟𝑐𝑒) t→汇点(𝑡𝑎𝑟𝑔𝑒𝑡) 𝒄→容量函数(𝑐𝑎𝑝𝑎𝑐𝑖𝑡𝑦)
流量函数
f
(
u
,
v
)
f(u,v)
f(u,v)
容量函数
c
(
u
,
v
)
c(u,v)
c(u,v)
剩余容量函数
r
(
u
,
v
)
=
c
(
u
,
v
)
−
f
(
u
,
v
)
r(u,v)=c(u,v)-f(u,v)
r(u,v)=c(u,v)−f(u,v)
对于流量函数 f ( u , v ) f(u,v) f(u,v),满足以下条件:
- 斜对称: f ( u , v ) = − f ( v , u ) f(u,v)=-f(v,u) f(u,v)=−f(v,u)
- 容量约束: f ( u , v ) = f ( u , v ) f(u,v)=f(u,v) f(u,v)=f(u,v)
- 流量守恒: ∀ u ∈ V − s , t , ∑ v ∈ V f ( u , v ) = 0 ∀u \in V-{s,t}, \sum_{v \in V}{f(u,v)=0} ∀u∈V−s,t,∑v∈Vf(u,v)=0
- 零自流量: f ( u , u ) = 0 f(u,u)=0 f(u,u)=0
顶点𝒗的级𝒍𝒆𝒗𝒆𝒍(𝒗):
由源点𝑠到顶点𝑣的通路中的最少的边数
分级图𝑳为(𝑽,𝑬′),其中𝑬′={(𝒖,𝒗)|𝒍𝒆𝒗𝒆𝒍(𝒗)=𝒍𝒆𝒗𝒆𝒍(𝒖)+𝟏}
最大流最小割定理:
对流网络 ( G , s , t , c ) (G,s,t,c) (G,s,t,c), f f f是 G G G中的流,则下面的命题等价:
- 存在一个容量为 c ( u , v ) = ∣ f ∣ c(u,v)=|f| c(u,v)=∣f∣的割集 S , T {S,T} S,T;
- f f f是 G G G中的最大流;
- 不存在 f f f的增广路径。
最大流求解
Ford_Fullkerson方法
根据最大流最小割定理,在寻找图𝐺的最大流时,可以令𝐺的初始流量𝑓=0,然后重复地在𝑓的剩余图中寻找一条增广路径,用该路径的瓶颈流量来扩张流量𝑓,直到剩余图中不存在增广路径为止。
最大容量增广(MCA,Maximum Capacity Augmentation)
根据Ford-Fullkerson方法求解最大流问题,不同点在于指定了增广路径的选取方法:搜索一条具有最大瓶颈容量的增广路径
来加快算法运行时间(贪婪算法)。
步骤:
- 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)∈E, r ( u , v ) = c ( u , v ) r(u,v)=c(u,v) r(u,v)=c(u,v);
- 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)∈E, f ( u , v ) = 0 f(u,v)=0 f(u,v)=0;
- 若剩余图𝑅中存在增广路径,找出使得瓶颈流量𝛿最大的增广路径𝑝,转4;不存在转6;
- 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)∈p,令 r ( u , v ) = r ( u , v ) − Δ r(u,v)=r(u,v)-\Delta r(u,v)=r(u,v)−Δ;
- 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)∈p,令 f ( u , v ) = f ( u , v ) + Δ f(u,v)=f(u,v)+\Delta f(u,v)=f(u,v)+Δ,转3;
- 返回最大流 f f f。
最短路径增广(EK算法,Edmond-Karp)
根据Ford-Fullkerson方法求解最大流问题,不同点在于指定了增广路径的选取方法:搜索一条具有最短路径(边最少)的增广路径
来加快算法运行时间。
步骤:
- 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)∈E, r ( u , v ) = c ( u , v ) r(u,v)=c(u,v) r(u,v)=c(u,v);
- 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)∈E, f ( u , v ) = 0 f(u,v)=0 f(u,v)=0;
- 按照分级图原理,
用BFS在剩余图中搜索由𝑠到𝑡的最短路径𝑝
,转4;不存在转6; - 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)∈p,令 r ( u , v ) = r ( u , v ) − Δ r(u,v)=r(u,v)-\Delta r(u,v)=r(u,v)−Δ;
- 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)∈p,令 f ( u , v ) = f ( u , v ) + Δ f(u,v)=f(u,v)+\Delta f(u,v)=f(u,v)+Δ,转3;
- 返回最大流 f f f。
MCA和EK算法的实现
举例
c ( u , v ) c(u,v) c(u,v)
源码
# -*- coding: utf-8 -*-
# Copyright (c) 2019 - Youpeng Hu <yoooooohu@foxmail.com>
import copy
class Weighted_Gragh:
def __init__(self, vertices):
self.adjacent_table = {}
for vertex in vertices:
self.adjacent_table.update({vertex: {}})
print("Weighted Gragh has been created Successfully!!!!!")
print('self.adjacent_table', end=" -> \n")
print(self.adjacent_table)
def addNeighbor(self, source, terminal, weigh):
neighbor_list = self.adjacent_table[source]
neighbor_list.update({terminal: weigh})
self.adjacent_table.update({source: neighbor_list})
print('Add a new Neighbor\nself.adjacent_table', end=" -> \n")
print(self.adjacent_table)
def maxCapacityAugmentation(self, source, terminal):
print('###################################')
print('Max Capacity Augmentation algorithm')
self.f_table = {}
self.r_table = copy.deepcopy(self.adjacent_table)
path = self.findMaxBottleneckDFS(source, terminal, 0, {})
while path:
print('path', end=" >>>>>>>>>>>>>>\n")
print(path)
bottleneck = self.foundBottleneck(path, terminal)
self.argumentFlow(bottleneck, path, terminal)
path = self.findMaxBottleneckDFS(source, terminal, 0, {})
return self.f_table
def findMaxBottleneckDFS(self, source, terminal, level, level_table, max_bottleneck = 1000):
print('(source, terminal, max_bottleneck)', end=" ->\n")
print((source, terminal, max_bottleneck))
level += 1
for end, weight in self.r_table[source].items():
if weight > 0:
if level not in level_table:
level_table[level] = {}
if end not in level_table[level]:
level_table[level][end] = {}
level_table[level][end] = {source: weight}
if end == terminal:
return level_table
if weight >= max_bottleneck:
return self.findMaxBottleneckDFS(end, terminal, level, level_table, max_bottleneck = max_bottleneck)
elif weight < max_bottleneck:
return self.findMaxBottleneckDFS(end, terminal, level, level_table, max_bottleneck = weight)
elif weight < 0:
raise ("the value of weigh have some problem")
def edmondKarp(self, source, terminal):
print('###################################')
print('edmondKarp algorithm')
self.f_table = {}
self.r_table = copy.deepcopy(self.adjacent_table)
path = self.findTerminalBFS([source], terminal, 0, {}, [source])
while path:
print('argument table', end=" >>>>>>>>>>>>>>\n")
print(path)
bottleneck = self.foundBottleneck(path, terminal)
self.argumentFlow(bottleneck, path, terminal)
path = self.findTerminalBFS([source], terminal, 0, {}, [source])
return self.f_table
def findTerminalBFS(self, start_list, terminal, level, level_table, visited):
print('(start_list, terminal, level, level_table, visited)', end=" ->\n")
print((start_list, terminal, level, level_table, visited))
level += 1
end_list = []
for start in start_list:
for end, weight in self.r_table[start].items():
if (weight > 0) & (end not in visited):
if level not in level_table:
level_table[level] = {}
if end not in level_table[level]:
level_table[level][end] = {}
level_table[level][end] = {start: weight}
if terminal == end:
return level_table
visited.append(end)
end_list.append(end)
elif weight < 0:
raise ("the value of weight have some problem")
if end_list:
return self.findTerminalBFS(end_list, terminal, level, level_table, visited)
def foundBottleneck(self, path, terminal):
weight_list = []
tmp_level = max(path.keys())
print('tmp_level', end =" ->")
print(tmp_level)
start = terminal
while tmp_level > 0:
for start, weight in path[tmp_level][start].items():
pass
tmp_level -= 1
weight_list.append(weight)
print('Bottleneck of traffic -> {}'.format(min(weight_list)))
return min(weight_list)
def argumentFlow(self, bottleneck, path, terminal):
tmp_level = max(path.keys())
end = terminal
print('path:\n', terminal, end="")
while tmp_level > 0:
for start, weight in path[tmp_level][end].items():
pass
print("->", start, weight, end="")
if start not in self.f_table:
self.f_table[start] = {}
if end not in self.f_table[start]:
self.f_table[start][end] = 0
self.f_table[start][end] += bottleneck
self.r_table[start][end] -= bottleneck
tmp_level -= 1
end = start
print()
print('self.r_table', end=" ->\n")
print(self.r_table)
print('self.f_table', end=" ->\n")
print(self.f_table)
if __name__ == '__main__':
gragh = Weighted_Gragh(['s','a','b','c','d','e','f','g','h','i','j','t'])
gragh.addNeighbor('s', 'a', 6)
gragh.addNeighbor('s', 'c', 8)
gragh.addNeighbor('a', 'b', 3)
gragh.addNeighbor('a', 'd', 3)
gragh.addNeighbor('b', 't', 10)
gragh.addNeighbor('c', 'd', 4)
gragh.addNeighbor('c', 'f', 4)
gragh.addNeighbor('d', 'e', 3)
gragh.addNeighbor('d', 'g', 6)
gragh.addNeighbor('e', 'b', 7)
gragh.addNeighbor('e', 'j', 4)
gragh.addNeighbor('f', 'h', 4)
gragh.addNeighbor('g', 'e', 7)
gragh.addNeighbor('h', 'g', 1)
gragh.addNeighbor('h', 'i', 3)
gragh.addNeighbor('i', 'j', 3)
gragh.addNeighbor('j', 't', 5)
gragh.edmondKarp('s', 't')
print('gragh.f_table for Edmond_Karp', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
print(gragh.f_table)
print('gragh.r_table for Edmond_Karp', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
print(gragh.r_table)
gragh.maxCapacityAugmentation('s', 't')
print('gragh.f_table for maxCapacityAugmentation', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
print(gragh.f_table)
print('gragh.r_table for maxCapacityAugmentation', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
print(gragh.r_table)
输出参考
无论对于MCA或EK算法,输出的r_table
gragh.r_table
{'s': {'a': 0, 'c': 0},
'a': {'b': 0, 'd': 0},
'b': {'t': 0},
'c': {'d': 0, 'f': 0},
'd': {'e': 0, 'g': 2},
'e': {'b': 0, 'j': 3},
'f': {'h': 0},
'g': {'e': 2},
'h': {'g': 0, 'i': 0},
'i': {'j': 0},
'j': {'t': 1},
't': {}
}
最大流
f
m
a
x
(
u
,
v
)
f_{max}(u,v)
fmax(u,v)
edited date: 2019-11-15