用 Python 实现最小割算法:Ford-Fulkerson 和 Stoer-Wagner
Ford-Fulkerson 算法:
- 首先定义一个函数,接受图、源点、汇点,返回最大流量和最大流量对应的路径。
def ford_fulkerson(graph, source, sink):
# 残量图
residual_graph = [[0 for _ in range(len(graph))] for _ in range(len(graph))]
for i in range(len(graph)):
for j in range(len(graph)):
residual_graph[i][j] = graph[i][j]
parent = [-1] * len(graph)
# bfs遍历寻找增广路径
def bfs(start, end):
visited = [False] * len(graph)
visited[start] = True
queue = []
queue.append((start))
while queue:
u = queue.pop(0)
for v in range(len(graph)):
if not visited[v] and residual_graph[u][v] > 0:
queue.append(v)
visited[v] = True
parent[v] = u
return visited[end]
max_flow = 0
while bfs(source, sink):
flow = float("inf")
v = sink
while v != source:
u = parent[v]
flow = min(flow, residual_graph[u][v])
v = u
v = sink
while v != source:
u = parent[v]
residual_graph[u][v] -= flow
residual_graph[v][u] += flow
v = u
max_flow += flow
return max_flow, parent
- 接下来定义一个函数,输入图,返回最小割。
def min_cut_ford_fulkerson(graph):
n = len(graph)
source = 0
sink = n - 1
# 首先获取源点到汇点的最大流量
max_flow, parent = ford_fulkerson(graph, source, sink)
# 找出剩余的不连通部分
min_cut = []
visited = [False] * n
visited[source] = True
queue = []
queue.append(source)
while queue:
u = queue.pop(0)
for v in range(len(graph)):
if not visited[v] and graph[u][v] > 0:
queue.append(v)
visited[v] = True
# 拆分为两个不连通的部分
for u in range(n):
for v in range(n):
if visited[u] and not visited[v] and graph[u][v] > 0:
min_cut.append((u, v))
return min_cut
Stoer-Wagner 算法:
- 首先定义一个函数,输入图,返回最小割。
def min_cut_stoer_wagner(graph):
n = len(graph)
# 初始化顶点集合
vertices = [i for i in range(n)]
# 初始化最小割为正无穷大
min_cut = float('inf')
while len(vertices) > 1:
# 初始化每个点的电荷值为0
charge = [0] * n
# 重复n-1次计算最短跨越边
for i in range(n - 1):
# 初始化最小边权为正无穷大
min_edge = float('inf')
# 选择最小边权的跨越边
for u in vertices:
if charge[u] > 0:
for v in vertices:
if charge[v] == 0 and graph[u][v] < min_edge:
min_edge = graph[u][v]
min_u = u
min_v = v
# 更新电荷值
charge[min_v] += min_edge
# 切分点
cut_vertex = vertices[1]
# 初始化最小割点集为空
min_cut_vertices = []
# 将每个点归入到源点or汇点中
source, sink = vertices[0], cut_vertex
visited = [False] * n
visited[source] = True
for _ in range(n - 1):
visited[cut_vertex] = True
# 将cut_vertex归入到源点集合(以source为代表)
min_cut_vertices.append(cut_vertex)
# 查找下一个切割点,谁的电荷值最大就归他
max_charge = -float('inf')
for u in vertices:
if not visited[u]:
charge[u] += graph[cut_vertex][u]
if charge[u] > max_charge:
max_charge = charge[u]
next_cut_vertex = u
cut_vertex = next_cut_vertex
# 记录最小割
cut = 0
for u in min_cut_vertices:
for v in range(n):
if v not in min_cut_vertices:
if graph[u][v] > 0:
cut += graph[u][v]
if cut < min_cut:
min_cut = cut
# 缩小顶点集合
vertices.remove(cut_vertex)
# 合并切割点前的所有点
for u in range(n):
graph[source][u] += graph[cut_vertex][u]
graph[u][source] += graph[u][cut_vertex]
return min_cut
相关文章