最大流最小割Dinic算法实现-有向图实例(实战)

 最大流最小割的概念可以参照以下网站,我这里给出一个实现,并举个例子说明

​​​​​​最小割 - OI Wiki

# 定义 Dinic 算法的类
class DinicGraph:
    def __init__(self, vertices):
        # 初始化具有指定顶点数的图。每个顶点有一个用于存储边的列表
        self.V = vertices
        self.graph = [[] for _ in range(vertices)]

    def add_edge(self, s, t, capacity):
        # 向图中添加一条边。s和t是边的顶点,capacity是边的容量。
        # 创建一条从s到t的边,容量为capacity,初始流量为0,记录反向边的索引
        self.graph[s].append([t, capacity, 0, len(self.graph[t])])
        # 创建反向边(从t到s),容量为0(反向流量),初始流量为0,记录正向边的索引
        self.graph[t].append([s, 0, 0, len(self.graph[s]) - 1])

    def bfs(self, s, t, level):
        # 使用广度优先搜索(BFS)构建层级图。判断是否可以从源点s到达汇点t。
        for i in range(len(level)):
            level[i] = -1  # 初始化所有节点的层级为-1
        level[s] = 0  # 源点的层级设为0

        queue = [s]  # BFS的队列,起始包含源点
        while queue:
            u = queue.pop(0)
            for v, cap, flow, rev in self.graph[u]:
                # 如果节点v未访问过,且u到v的边未满载,则将v加入队列
                if level[v] < 0 and flow < cap:
                    level[v] = level[u] + 1
                    queue.append(v)
        return level[t] >= 0  # 如果汇点t被访问过,则返回True

    def dfs(self, u, t, flow, level, start):
        # 使用深度优先搜索(DFS)寻找增广路径。
        # u是当前节点,t是汇点,flow是当前路径的流量,level是层级图,start记录搜索的起点
        if u == t:
            return flow  # 如果到达汇点,返回流量

        for i in range(start[u], len(self.graph[u])):
            v, cap, flow_v, rev = self.graph[u][i]

            # 检查是否存在增广路径:v在下一层级,且u到v的边未满载
            if level[v] == level[u] + 1 and flow_v < cap:
                cur_flow = min(flow, cap - flow_v)  # 可增加的流量
                temp_flow = self.dfs(v, t, cur_flow, level, start)  # 递归调用DFS

                if temp_flow > 0:
                    # 更新正向和反向边的流量
                    self.graph[u][i][2] += temp_flow
                    self.graph[v][rev][2] -= temp_flow
                    return temp_flow
            start[u] += 1  # 更新起点

        return 0  # 如果没有增广路径,返回0

    def dinic_max_flow(self, s, t):
        # 计算从源点s到汇点t的最大流
        max_flow = 0
        level = [-1] * self.V  # 初始化层级图

        while self.bfs(s, t, level):  # 只要存在从s到t的路径
            start = [0] * self.V  # 初始化搜索的起点
            while True:
                flow = self.dfs(s, t, float('inf'), level, start)  # 通过DFS寻找增广路径
                if flow == 0:
                    break  # 如果没有增广路径,结束循环
                max_flow += flow  # 累加流量
        return max_flow  # 返回最大流

 以下实例代码

import matplotlib.pyplot as plt
import networkx as nx

# Compute the max flow
max_flow = g.dinic_max_flow(0, 5)

# Create a NetworkX graph for visualization
G = nx.DiGraph()
for u in range(len(g.graph)):
    for v, cap, flow, _ in g.graph[u]:
        if cap > 0:
            G.add_edge(u, v, capacity=cap, flow=flow)

# Drawing the capacity graph
plt.figure(figsize=(12, 6))

plt.subplot(1, 2, 1)
edge_labels = {(u, v): f"{d['capacity']}" for u, v, d in G.edges(data=True)}
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_color='skyblue', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.title('Capacity Graph')

# Drawing the flow graph
plt.subplot(1, 2, 2)
edge_labels = {(u, v): f"{d['flow']}" for u, v, d in G.edges(data=True)}
nx.draw(G, pos, with_labels=True, node_color='lightgreen', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.title(f'Flow Graph (Max Flow: {max_flow})')

plt.tight_layout()
plt.show()

以下左图为容量图,右图为最大流图。

这里展示了一个包含6个节点的网络的容量图和最大流图,这是基于上述的Dinic算法实现的:

容量图:这张图显示了网络中节点间边的原始容量。边上的数字代表每条边的容量。

流量图:这张图展示了从源点(节点0)到汇点(节点5)计算出的最大流后,每条边上的流量。边上的数字代表通过每条边的流量。算法计算出的这个网络的最大流量在图的标题中显示。

接下来计算最小割,以下是代码

def find_min_cut(dinic_graph, s):
    """
    Find the minimum cut in the graph.
    :param dinic_graph: The graph with maximum flow already computed.
    :param s: The source vertex.
    :return: A tuple containing the set of vertices reachable from s and the min-cut edges.
    """
    # Perform a BFS from the source to find all reachable vertices
    reachable = [False] * dinic_graph.V
    queue = [s]
    reachable[s] = True

    while queue:
        u = queue.pop(0)
        for v, cap, flow, _ in dinic_graph.graph[u]:
            # If there is remaining capacity and the vertex is not yet visited
            if not reachable[v] and cap - flow > 0:
                queue.append(v)
                reachable[v] = True

    # Find the edges that are from a reachable vertex to a non-reachable vertex
    min_cut_edges = []
    for u in range(dinic_graph.V):
        for v, cap, flow, _ in dinic_graph.graph[u]:
            if reachable[u] and not reachable[v] and cap > 0:
                min_cut_edges.append((u, v))

    return reachable, min_cut_edges

# Using the previously created DinicGraph instance 'g'
reachable, min_cut_edges = find_min_cut(g, 0)

# Create a NetworkX graph for the residual graph visualization
G_residual = nx.DiGraph()
for u in range(len(g.graph)):
    for v, cap, flow, _ in g.graph[u]:
        if cap > 0:
            residual_capacity = cap - flow
            if residual_capacity > 0:  # Add edges with residual capacity
                G_residual.add_edge(u, v, capacity=residual_capacity)

# Drawing the residual graph
plt.figure(figsize=(8, 6))
edge_labels = {(u, v): f"{d['capacity']}" for u, v, d in G_residual.edges(data=True)}
pos = nx.spring_layout(G_residual)
nx.draw(G_residual, pos, with_labels=True, node_color='lightblue', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G_residual, pos, edge_labels=edge_labels)
plt.title('Residual Graph after Max Flow')

plt.show()

reachable, min_cut_edges

以下是残差图

在计算了最大流后,我们可以找到网络的最小割。以下是残差图和最小割的结果:

残差图:这张图显示了计算最大流后的残差图。每条边上的数字表示该边的残余容量(原始容量减去流量)。只有残余容量大于0的边被显示。

最小割结果:最小割是一组边,其移除会断开源点到汇点的所有路径。在这个例子中,最小割包含的边是:(1, 3), (4, 3), (4, 5)。这意味着通过切断这些边,我们可以阻断从源点到汇点的所有路径。

在图中,可以看到源点可以到达的节点(1, 2, 4)和它不能到达的节点(3, 5)。最小割的边正是从可达节点到不可达节点的那些边。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值