请求推荐删除容量为1的边后高效重算maxflow的实现方案
Great question! When dealing with deleting a capacity-1 edge from a flow network and recomputing max flow efficiently, you don’t need to start from scratch—you can leverage the original max flow’s residual network to avoid redundant work. This approach runs in linear time O(V+E), which is way faster than re-running a full max flow algorithm on large graphs.
Key Insight
Since the edge has capacity 1, there are only two cases to consider:
- The edge carries 0 flow in the original max flow: Deleting it has no impact on the max flow—you can just reuse the original result.
- The edge carries 1 flow (its full capacity): You need to check if you can reroute this 1 unit of flow through an alternative path. If yes, the max flow stays the same; if no, the max flow decreases by 1.
Python Implementation with igraph
Here’s a step-by-step implementation using igraph’s built-in max flow tools:
Step 1: Compute Original Max Flow
First, calculate the initial max flow and get the residual network:
import igraph as ig # Assume your graph is already built, with 's' as source and 't' as sink g = ig.Graph(directed=True) # ... add nodes and edges with 'capacity' attribute ... s = 0 # Example source node index t = len(g.vs) - 1 # Example sink node index # Compute original max flow mf = g.maxflow(s, t) original_flow = mf.flow_value
Step 2: Handle Edge Deletion
Target the edge you want to delete, check its flow, and compute the new max flow:
# Identify the edge to delete (replace with your edge index or source/target) edge_index = 5 # Example edge index e = g.es[edge_index] u = e.source v = e.target edge_flow = mf.flow[edge_index] if edge_flow == 0: # Edge wasn't used in the max flow—deletion has no effect new_max_flow = original_flow else: # Get the residual network from the original max flow residual_g = mf.residual_graph() # Disable the original edge in the residual network (since we're deleting it) try: res_edge_id = residual_g.get_eid(u, v) residual_g.es[res_edge_id]['capacity'] = 0 except ig.InternalError: # Edge doesn't exist in residual network (already at 0 capacity) pass # BFS to find an alternative augmenting path (capacity ≥1) def bfs_find_path(graph, start, end): visited = {node: False for node in graph.vs.indices} parent = {node: None for node in graph.vs.indices} queue = [start] visited[start] = True while queue: current = queue.pop(0) if current == end: break # Iterate over outgoing edges with remaining capacity for neighbor in graph.neighbors(current, mode='out'): edge = graph.es[graph.get_eid(current, neighbor, directed=True)] if not visited[neighbor] and edge['capacity'] > 0: visited[neighbor] = True parent[neighbor] = current queue.append(neighbor) # Reconstruct path if found path = [] current = end while parent[current] is not None: path.append((parent[current], current)) current = parent[current] return path if path else None alternative_path = bfs_find_path(residual_g, s, t) new_max_flow = original_flow if alternative_path else original_flow - 1 print(f"Original max flow: {original_flow}, New max flow: {new_max_flow}")
C Language Implementation
For even better performance, you can implement this logic in C using an adjacency list for the residual network. Here’s a condensed outline:
Step 1: Represent the Residual Network
Use a struct to represent edges in the residual network:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <limits.h> typedef struct Edge { int to, rev; // Target node, index of reverse edge in adjacency list int cap; // Remaining capacity } Edge; typedef struct Graph { int num_nodes; Edge **adj; int *adj_size; // Track size of each adjacency list } Graph; // Helper to create a new graph Graph* create_graph(int n) { Graph *g = malloc(sizeof(Graph)); g->num_nodes = n; g->adj = malloc(n * sizeof(Edge*)); g->adj_size = calloc(n, sizeof(int)); for (int i=0; i<n; i++) { g->adj[i] = malloc(10 * sizeof(Edge)); // Initial buffer size } return g; } // Helper to add an edge to the graph void add_edge(Graph *g, int from, int to, int cap) { // Add forward edge Edge forward = {to, g->adj_size[to], cap}; g->adj[from][g->adj_size[from]++] = forward; // Add reverse edge Edge reverse = {from, g->adj_size[from]-1, 0}; g->adj[to][g->adj_size[to]++] = reverse; }
Step 2: Compute Original Max Flow (Edmonds-Karp)
Implement Edmonds-Karp to get the initial max flow and residual network:
// BFS for Edmonds-Karp bool bfs(Graph *g, int s, int t, int *parent, int *edge_idx) { bool *visited = calloc(g->num_nodes, sizeof(bool)); int *queue = malloc(g->num_nodes * sizeof(int)); int front = 0, rear = 0; queue[rear++] = s; visited[s] = true; parent[s] = -1; while (front < rear) { int u = queue[front++]; for (int i=0; i<g->adj_size[u]; i++) { Edge *e = &g->adj[u][i]; if (!visited[e->to] && e->cap > 0) { visited[e->to] = true; parent[e->to] = u; edge_idx[e->to] = i; queue[rear++] = e->to; if (e->to == t) { free(visited); free(queue); return true; } } } } free(visited); free(queue); return false; } // Edmonds-Karp algorithm int edmonds_karp(Graph *g, int s, int t) { int flow = 0; int *parent = malloc(g->num_nodes * sizeof(int)); int *edge_idx = malloc(g->num_nodes * sizeof(int)); while (bfs(g, s, t, parent, edge_idx)) { int min_cap = INT_MAX; // Find minimum capacity along the path for (int v = t; v != s; v = parent[v]) { int u = parent[v]; Edge *e = &g->adj[u][edge_idx[v]]; if (e->cap < min_cap) min_cap = e->cap; } // Augment the flow for (int v = t; v != s; v = parent[v]) { int u = parent[v]; Edge *e = &g->adj[u][edge_idx[v]]; e->cap -= min_cap; g->adj[v][e->rev].cap += min_cap; } flow += min_cap; } free(parent); free(edge_idx); return flow; }
Step 3: Handle Edge Deletion
After computing the original max flow, locate the edge to delete, set its capacity to 0, and run BFS to check for an alternative path:
int recompute_max_flow_after_deletion(Graph *g, int s, int t, int original_flow, int u, int v) { // Find and disable the forward edge u->v for (int i=0; i<g->adj_size[u]; i++) { Edge *e = &g->adj[u][i]; if (e->to == v) { e->cap = 0; break; } } // Check for alternative augmenting path int *parent = malloc(g->num_nodes * sizeof(int)); int *edge_idx = malloc(g->num_nodes * sizeof(int)); bool has_alternative = bfs(g, s, t, parent, edge_idx); free(parent); free(edge_idx); return has_alternative ? original_flow : original_flow - 1; }
This approach avoids the overhead of re-computing max flow from scratch, making it ideal for large graphs where efficiency matters.
内容的提问来源于stack exchange,提问作者ivan

