📜  在流网络中找到最小 st 割

📅  最后修改于: 2022-05-13 01:57:54.503000             🧑  作者: Mango

在流网络中找到最小 st 割

在流网络中,st 割是要求源“s”和汇“t”在不同子集中的割,它由从源端到汇端的边组成。 st 割的容量由割集中每条边的容量之和定义。 (来源:维基)
这里讨论的问题是找到给定网络的最小容量 st cut。预期输出是最小割的所有边。

例如,在下面的流网络中,示例 st 割是 {{0 ,1}, {0, 2}}, {{0, 2}, {1, 2}, {1, 3}} 等。最小 st 割是 {{1, 3}, {4, 3}, {4 5}},容量为 12+7+4 = 23。

minCut

我们强烈建议您先阅读以下帖子。
最大流量问题的 Ford-Fulkerson 算法

最小切割和最大流量
与最大二分匹配一样,这是另一个可以使用 Ford-Fulkerson 算法解决的问题。这是基于最大流最小割定理。

最大流最小割定理指出,在流网络中,最大流的量等于最小割的容量。有关该定理的证明,请参见 CLRS 书籍。

从 Ford-Fulkerson,我们得到了最小切割容量。如何打印形成最小切口的所有边缘?这个想法是使用残差图。

以下是打印最小切口的所有边缘的步骤。

1)运行 Ford-Fulkerson 算法并考虑最终的残差图。

2)在残差图中找到从源可达的顶点集合。

3)从可达顶点到不可达顶点的所有边都是最小割边。打印所有这些边缘。

以下是上述方法的实现。

C++
// C++ program for finding minimum cut using Ford-Fulkerson
#include 
#include 
#include 
#include 
using namespace std;
  
// Number of vertices in given graph
#define V 6
  
/* Returns true if there is a path from source 's' to sink 't' in
  residual graph. Also fills parent[] to store the path */
int bfs(int rGraph[V][V], int s, int t, int parent[])
{
    // Create a visited array and mark all vertices as not visited
    bool visited[V];
    memset(visited, 0, sizeof(visited));
  
    // Create a queue, enqueue source vertex and mark source vertex
    // as visited
    queue  q;
    q.push(s);
    visited[s] = true;
    parent[s] = -1;
  
    // Standard BFS Loop
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
  
        for (int v=0; v 0)
            {
                q.push(v);
                parent[v] = u;
                visited[v] = true;
            }
        }
    }
  
    // If we reached sink in BFS starting from source, then return
    // true, else false
    return (visited[t] == true);
}
  
// A DFS based function to find all reachable vertices from s.  The function
// marks visited[i] as true if i is reachable from s.  The initial values in
// visited[] must be false. We can also use BFS to find reachable vertices
void dfs(int rGraph[V][V], int s, bool visited[])
{
    visited[s] = true;
    for (int i = 0; i < V; i++)
       if (rGraph[s][i] && !visited[i])
           dfs(rGraph, i, visited);
}
  
// Prints the minimum s-t cut
void minCut(int graph[V][V], int s, int t)
{
    int u, v;
  
    // Create a residual graph and fill the residual graph with
    // given capacities in the original graph as residual capacities
    // in residual graph
    int rGraph[V][V]; // rGraph[i][j] indicates residual capacity of edge i-j
    for (u = 0; u < V; u++)
        for (v = 0; v < V; v++)
             rGraph[u][v] = graph[u][v];
  
    int parent[V];  // This array is filled by BFS and to store path
  
    // Augment the flow while there is a path from source to sink
    while (bfs(rGraph, s, t, parent))
    {
        // Find minimum residual capacity of the edhes along the
        // path filled by BFS. Or we can say find the maximum flow
        // through the path found.
        int path_flow = INT_MAX;
        for (v=t; v!=s; v=parent[v])
        {
            u = parent[v];
            path_flow = min(path_flow, rGraph[u][v]);
        }
  
        // update residual capacities of the edges and reverse edges
        // along the path
        for (v=t; v != s; v=parent[v])
        {
            u = parent[v];
            rGraph[u][v] -= path_flow;
            rGraph[v][u] += path_flow;
        }
    }
  
    // Flow is maximum now, find vertices reachable from s
    bool visited[V];
    memset(visited, false, sizeof(visited));
    dfs(rGraph, s, visited);
  
    // Print all edges that are from a reachable vertex to
    // non-reachable vertex in the original graph
    for (int i = 0; i < V; i++)
      for (int j = 0; j < V; j++)
         if (visited[i] && !visited[j] && graph[i][j])
              cout << i << " - " << j << endl;
  
    return;
}
  
// Driver program to test above functions
int main()
{
    // Let us create a graph shown in the above example
    int graph[V][V] = { {0, 16, 13, 0, 0, 0},
                        {0, 0, 10, 12, 0, 0},
                        {0, 4, 0, 0, 14, 0},
                        {0, 0, 9, 0, 0, 20},
                        {0, 0, 0, 7, 0, 4},
                        {0, 0, 0, 0, 0, 0}
                      };
  
    minCut(graph, 0, 5);
  
    return 0;
}


Java
// Java program for finding min-cut in the given graph
import java.util.LinkedList;
import java.util.Queue;
  
public class Graph {
          
    // Returns true if there is a path
    // from source 's' to sink 't' in residual 
    // graph. Also fills parent[] to store the path 
    private static boolean bfs(int[][] rGraph, int s,
                                int t, int[] parent) {
          
        // Create a visited array and mark 
        // all vertices as not visited     
        boolean[] visited = new boolean[rGraph.length];
          
        // Create a queue, enqueue source vertex
        // and mark source vertex as visited     
        Queue q = new LinkedList();
        q.add(s);
        visited[s] = true;
        parent[s] = -1;
          
        // Standard BFS Loop     
        while (!q.isEmpty()) {
            int v = q.poll();
            for (int i = 0; i < rGraph.length; i++) {
                if (rGraph[v][i] > 0 && !visited[i]) {
                    q.offer(i);
                    visited[i] = true;
                    parent[i] = v;
                }
            }
        }
          
        // If we reached sink in BFS starting 
        // from source, then return true, else false     
        return (visited[t] == true);
    }
      
    // A DFS based function to find all reachable 
    // vertices from s. The function marks visited[i] 
    // as true if i is reachable from s. The initial 
    // values in visited[] must be false. We can also 
    // use BFS to find reachable vertices
    private static void dfs(int[][] rGraph, int s,
                                boolean[] visited) {
        visited[s] = true;
        for (int i = 0; i < rGraph.length; i++) {
                if (rGraph[s][i] > 0 && !visited[i]) {
                    dfs(rGraph, i, visited);
                }
        }
    }
  
    // Prints the minimum s-t cut
    private static void minCut(int[][] graph, int s, int t) {
        int u,v;
          
        // Create a residual graph and fill the residual 
        // graph with given capacities in the original 
        // graph as residual capacities in residual graph
        // rGraph[i][j] indicates residual capacity of edge i-j
        int[][] rGraph = new int[graph.length][graph.length]; 
        for (int i = 0; i < graph.length; i++) {
            for (int j = 0; j < graph.length; j++) {
                rGraph[i][j] = graph[i][j];
            }
        }
  
        // This array is filled by BFS and to store path
        int[] parent = new int[graph.length]; 
          
        // Augment the flow while tere is path from source to sink     
        while (bfs(rGraph, s, t, parent)) {
              
            // Find minimum residual capacity of the edhes 
            // along the path filled by BFS. Or we can say 
            // find the maximum flow through the path found.
            int pathFlow = Integer.MAX_VALUE;         
            for (v = t; v != s; v = parent[v]) {
                u = parent[v];
                pathFlow = Math.min(pathFlow, rGraph[u][v]);
            }
              
            // update residual capacities of the edges and 
            // reverse edges along the path
            for (v = t; v != s; v = parent[v]) {
                u = parent[v];
                rGraph[u][v] = rGraph[u][v] - pathFlow;
                rGraph[v][u] = rGraph[v][u] + pathFlow;
            }
        }
          
        // Flow is maximum now, find vertices reachable from s     
        boolean[] isVisited = new boolean[graph.length];     
        dfs(rGraph, s, isVisited);
          
        // Print all edges that are from a reachable vertex to
        // non-reachable vertex in the original graph     
        for (int i = 0; i < graph.length; i++) {
            for (int j = 0; j < graph.length; j++) {
                if (graph[i][j] > 0 && isVisited[i] && !isVisited[j]) {
                    System.out.println(i + " - " + j);
                }
            }
        }
    }
  
    //Driver Program
    public static void main(String args[]) {
          
        // Let us create a graph shown in the above example
        int graph[][] = { {0, 16, 13, 0, 0, 0},
                {0, 0, 10, 12, 0, 0},
                {0, 4, 0, 0, 14, 0},
                {0, 0, 9, 0, 0, 20},
                {0, 0, 0, 7, 0, 4},
                {0, 0, 0, 0, 0, 0}
            };
        minCut(graph, 0, 5);
    }
}
// This code is contributed by Himanshu Shekhar


Python
# Python program for finding min-cut in the given graph 
# Complexity : (E*(V^3)) 
# Total augmenting path = VE and BFS 
# with adj matrix takes :V^2 times 
  
from collections import defaultdict 
  
# This class represents a directed graph
# using adjacency matrix representation 
class Graph: 
  
    def __init__(self,graph): 
        self.graph = graph # residual graph 
        self.org_graph = [i[:] for i in graph] 
        self. ROW = len(graph) 
        self.COL = len(graph[0]) 
  
  
    '''Returns true if there is a path from 
    source 's' to sink 't' in 
    residual graph. Also fills 
    parent[] to store the path '''
    def BFS(self,s, t, parent): 
  
        # Mark all the vertices as not visited 
        visited =[False]*(self.ROW) 
  
        # Create a queue for BFS 
        queue=[] 
  
        # Mark the source node as visited and enqueue it 
        queue.append(s) 
        visited[s] = True
  
        # Standard BFS Loop 
        while queue: 
  
            #Dequeue a vertex from queue and print it 
            u = queue.pop(0) 
  
            # Get all adjacent vertices of 
            # the dequeued vertex u 
            # If a adjacent has not been
            # visited, then mark it 
            # visited and enqueue it 
            for ind, val in enumerate(self.graph[u]): 
                if visited[ind] == False and val > 0 : 
                    queue.append(ind) 
                    visited[ind] = True
                    parent[ind] = u 
  
        # If we reached sink in BFS starting
        # from source, then return 
        # true, else false 
        return True if visited[t] else False
          
    # Function for Depth first search 
    # Traversal of the graph
    def dfs(self, graph,s,visited):
        visited[s]=True
        for i in range(len(graph)):
            if graph[s][i]>0 and not visited[i]:
                self.dfs(graph,i,visited)
  
    # Returns the min-cut of the given graph 
    def minCut(self, source, sink): 
  
        # This array is filled by BFS and to store path 
        parent = [-1]*(self.ROW) 
  
        max_flow = 0 # There is no flow initially 
  
        # Augment the flow while there is path from source to sink 
        while self.BFS(source, sink, parent) : 
  
            # Find minimum residual capacity of the edges along the 
            # path filled by BFS. Or we can say find the maximum flow 
            # through the path found. 
            path_flow = float("Inf") 
            s = sink 
            while(s != source): 
                path_flow = min (path_flow, self.graph[parent[s]][s]) 
                s = parent[s] 
  
            # Add path flow to overall flow 
            max_flow += path_flow 
  
            # update residual capacities of the edges and reverse edges 
            # along the path 
            v = sink 
            while(v != source): 
                u = parent[v] 
                self.graph[u][v] -= path_flow 
                self.graph[v][u] += path_flow 
                v = parent[v] 
  
        visited=len(self.graph)*[False]
        self.dfs(self.graph,s,visited)
  
        # print the edges which initially had weights 
        # but now have 0 weight 
        for i in range(self.ROW): 
            for j in range(self.COL): 
                if self.graph[i][j] == 0 and\
                self.org_graph[i][j] > 0 and visited[i]: 
                    print str(i) + " - " + str(j) 
  
  
# Create a graph given in the above diagram 
graph = [[0, 16, 13, 0, 0, 0], 
        [0, 0, 10, 12, 0, 0], 
        [0, 4, 0, 0, 14, 0], 
        [0, 0, 9, 0, 0, 20], 
        [0, 0, 0, 7, 0, 4], 
        [0, 0, 0, 0, 0, 0]] 
  
g = Graph(graph) 
  
source = 0; sink = 5
  
g.minCut(source, sink) 
  
# This code is contributed by Neelam Yadav


C#
// C# program for finding min-cut in the given graph
using System;
using System.Collections.Generic;
  
class Graph
{
          
    // Returns true if there is a path
    // from source 's' to sink 't' in residual 
    // graph. Also fills parent[] to store the path 
    private static bool bfs(int[,] rGraph, int s,
                            int t, int[] parent) 
    {
          
        // Create a visited array and mark 
        // all vertices as not visited     
        bool[] visited = new bool[rGraph.Length];
          
        // Create a queue, enqueue source vertex
        // and mark source vertex as visited     
        Queue q = new Queue();
        q.Enqueue(s);
        visited[s] = true;
        parent[s] = -1;
          
        // Standard BFS Loop     
        while (q.Count != 0) 
        {
            int v = q.Dequeue();
            for (int i = 0; i < rGraph.GetLength(0); i++) 
            {
                if (rGraph[v,i] > 0 && !visited[i]) 
                {
                    q.Enqueue(i);
                    visited[i] = true;
                    parent[i] = v;
                }
            }
        }
          
        // If we reached sink in BFS starting 
        // from source, then return true, else false     
        return (visited[t] == true);
    }
      
    // A DFS based function to find all reachable 
    // vertices from s. The function marks visited[i] 
    // as true if i is reachable from s. The initial 
    // values in visited[] must be false. We can also 
    // use BFS to find reachable vertices
    private static void dfs(int[,] rGraph, int s,
                            bool[] visited) 
    {
        visited[s] = true;
        for (int i = 0; i < rGraph.GetLength(0); i++) 
        {
            if (rGraph[s,i] > 0 && !visited[i])
            {
                dfs(rGraph, i, visited);
            }
        }
    }
  
    // Prints the minimum s-t cut
    private static void minCut(int[,] graph, int s, int t) 
    {
        int u, v;
          
        // Create a residual graph and fill the residual 
        // graph with given capacities in the original 
        // graph as residual capacities in residual graph
        // rGraph[i,j] indicates residual capacity of edge i-j
        int[,] rGraph = new int[graph.Length,graph.Length]; 
        for (int i = 0; i < graph.GetLength(0); i++)
        {
            for (int j = 0; j < graph.GetLength(1); j++)
            {
                rGraph[i, j] = graph[i, j];
            }
        }
  
        // This array is filled by BFS and to store path
        int[] parent = new int[graph.Length]; 
          
        // Augment the flow while there is path
        // from source to sink     
        while (bfs(rGraph, s, t, parent)) 
        {
              
            // Find minimum residual capacity of the edhes 
            // along the path filled by BFS. Or we can say 
            // find the maximum flow through the path found.
            int pathFlow = int.MaxValue;         
            for (v = t; v != s; v = parent[v]) 
            {
                u = parent[v];
                pathFlow = Math.Min(pathFlow, rGraph[u, v]);
            }
              
            // update residual capacities of the edges and 
            // reverse edges along the path
            for (v = t; v != s; v = parent[v])
            {
                u = parent[v];
                rGraph[u, v] = rGraph[u, v] - pathFlow;
                rGraph[v, u] = rGraph[v, u] + pathFlow;
            } 
        }
          
        // Flow is maximum now, find vertices reachable from s     
        bool[] isVisited = new bool[graph.Length];     
        dfs(rGraph, s, isVisited);
          
        // Print all edges that are from a reachable vertex to
        // non-reachable vertex in the original graph     
        for (int i = 0; i < graph.GetLength(0); i++) 
        {
            for (int j = 0; j < graph.GetLength(1); j++)
            {
                if (graph[i, j] > 0 && 
                    isVisited[i] && !isVisited[j])
                {
                    Console.WriteLine(i + " - " + j);
                }
            }
        }
    }
  
    // Driver Code
    public static void Main(String []args)
    {
          
        // Let us create a graph shown 
        // in the above example
        int [,]graph = {{0, 16, 13, 0, 0, 0},
                        {0, 0, 10, 12, 0, 0},
                        {0, 4, 0, 0, 14, 0},
                        {0, 0, 9, 0, 0, 20},
                        {0, 0, 0, 7, 0, 4},
                        {0, 0, 0, 0, 0, 0}};
        minCut(graph, 0, 5);
    }
}
  
// This code is contributed by PrinciRaj1992



输出:
1 - 3
4 - 3
4 - 5

参考:
http://www.stanford.edu/class/cs97si/08-network-flow-problems.pdf
http://www.cs.princeton.edu/courses/archive/spring06/cos226/lectures/maxflow.pdf