📜  使用Bellman Ford算法从图的最小成本最大流

📅  最后修改于: 2021-05-24 16:09:49             🧑  作者: Mango

给定一个源节点S,一个宿节点T ,两个表示图表的矩阵Cap [] []Cost [] [] ,其中Cap [i] [j]是从节点i到节点j的有向边的容量,以及cost [i] [j]是沿着有向边从节点i到节点j发送一个单位流量的成本,任务是从给定图中找到具有最小成本最大流量的流量。

例子:

方法:
成本网络中的负周期是循环的,周期中所有边的成本之和为负。可以使用Bellman Ford算法检测到它们。应将其消除,因为实际上不允许通过此类循环。考虑一个负成本周期,如果所有流程都必须经过这个周期,则总成本始终在每个完成的周期中减少。这将导致希望将总成本降至最低的无限循环。因此,每当成本网络包含负周期时,这意味着可以将成本进一步最小化(通过流过周期的另一侧而不是当前考虑的那一侧)。一旦使瓶颈容量流经周期中的所有边缘,就可以消除一旦检测到的负周期。

现在,看看什么是供需节点:

通过向瓶颈周期的所有边缘发送瓶颈容量来解决负周期问题,可以解决给定的问题。另外,由于涉及需求节点,因此调用了Bellman Ford算法。

请按照以下步骤解决问题:

  • 将边缘容量和该边缘成本存储在两个单独的阵列中。
  • 给定源节点S和宿节点T ,拾取边缘p i ,需求节点d a和节点dist之间的距离,搜索是否可能有从ST的流动。
  • 如果存在流,则计算距离,值= dist + pi – pi [k] – cost [k]
  • dist []中的距离值与value进行比较,并不断进行更新,直到获得最小流量为止。

下面是上述方法的实现:

Java
// Java Program to implement
// the above approach
import java.util.*;
 
public class MinCostMaxFlow {
 
    // Stores the found edges
    boolean found[];
 
    // Stores the number of nodes
    int N;
 
    // Stores the capacity
    // of each edge
    int cap[][];
 
    int flow[][];
 
    // Stores the cost per
    // unit flow of each edge
    int cost[][];
 
    // Stores the distance from each node
    // and picked edges for each node
    int dad[], dist[], pi[];
 
    static final int INF
        = Integer.MAX_VALUE / 2 - 1;
 
    // Function to check if it is possible to
    // have a flow from the src to sink
    boolean search(int src, int sink)
    {
 
        // Initialise found[] to false
        Arrays.fill(found, false);
 
        // Initialise the dist[] to INF
        Arrays.fill(dist, INF);
 
        // Distance from the source node
        dist[src] = 0;
 
        // Iterate untill src reaches N
        while (src != N) {
 
            int best = N;
            found[src] = true;
 
            for (int k = 0; k < N; k++) {
 
                // If already found
                if (found[k])
                    continue;
 
                // Evaluate while flow
                // is still in supply
                if (flow[k][src] != 0) {
 
                    // Obtain the total value
                    int val
                        = dist[src] + pi[src]
                          - pi[k] - cost[k][src];
 
                    // If dist[k] is > minimum value
                    if (dist[k] > val) {
 
                        // Update
                        dist[k] = val;
                        dad[k] = src;
                    }
                }
 
                if (flow[src][k] < cap[src][k]) {
 
                    int val = dist[src] + pi[src]
                              - pi[k] + cost[src][k];
 
                    // If dist[k] is > minimum value
                    if (dist[k] > val) {
 
                        // Update
                        dist[k] = val;
                        dad[k] = src;
                    }
                }
 
                if (dist[k] < dist[best])
                    best = k;
            }
 
            // Update src to best for
            // next iteration
            src = best;
        }
 
        for (int k = 0; k < N; k++)
            pi[k]
                = Math.min(pi[k] + dist[k],
                           INF);
 
        // Return the value obtained at sink
        return found[sink];
    }
 
    // Function to obtain the maximum Flow
    int[] getMaxFlow(int cap[][], int cost[][],
                     int src, int sink)
    {
 
        this.cap = cap;
        this.cost = cost;
 
        N = cap.length;
        found = new boolean[N];
        flow = new int[N][N];
        dist = new int[N + 1];
        dad = new int[N];
        pi = new int[N];
 
        int totflow = 0, totcost = 0;
 
        // If a path exist from src to sink
        while (search(src, sink)) {
 
            // Set the default amount
            int amt = INF;
            for (int x = sink; x != src; x = dad[x])
 
                amt = Math.min(amt,
                               flow[x][dad[x]] != 0
                                   ? flow[x][dad[x]]
                                   : cap[dad[x]][x]
                                         - flow[dad[x]][x]);
 
            for (int x = sink; x != src; x = dad[x]) {
 
                if (flow[x][dad[x]] != 0) {
                    flow[x][dad[x]] -= amt;
                    totcost -= amt * cost[x][dad[x]];
                }
                else {
                    flow[dad[x]][x] += amt;
                    totcost += amt * cost[dad[x]][x];
                }
            }
            totflow += amt;
        }
 
        // Return pair total cost and sink
        return new int[] { totflow, totcost };
    }
 
    // Driver Code
    public static void main(String args[])
    {
 
        // Creating an object flow
        MinCostMaxFlow flow = new MinCostMaxFlow();
 
        int s = 0, t = 4;
 
        int cap[][] = { { 0, 3, 1, 0, 3 },
                        { 0, 0, 2, 0, 0 },
                        { 0, 0, 0, 1, 6 },
                        { 0, 0, 0, 0, 2 },
                        { 0, 0, 0, 0, 0 } };
 
        int cost[][] = { { 0, 1, 0, 0, 2 },
                         { 0, 0, 0, 3, 0 },
                         { 0, 0, 0, 0, 0 },
                         { 0, 0, 0, 0, 1 },
                         { 0, 0, 0, 0, 0 } };
 
        int ret[] = flow.getMaxFlow(cap, cost, s, t);
 
        System.out.println(ret[0] + " " + ret[1]);
    }
}


Python3
# Python3 program to implement
# the above approach
from sys import maxsize
from typing import List
 
# Stores the found edges
found = []
 
# Stores the number of nodes
N = 0
 
# Stores the capacity
# of each edge
cap = []
 
flow = []
 
# Stores the cost per
# unit flow of each edge
cost = []
 
# Stores the distance from each node
# and picked edges for each node
dad = []
dist = []
pi = []
 
INF = maxsize // 2 - 1
 
# Function to check if it is possible to
# have a flow from the src to sink
def search(src: int, sink: int) -> bool:
 
    # Initialise found[] to false
    found = [False for _ in range(N)]
 
    # Initialise the dist[] to INF
    dist = [INF for _ in range(N + 1)]
 
    # Distance from the source node
    dist[src] = 0
 
    # Iterate untill src reaches N
    while (src != N):
        best = N
        found[src] = True
 
        for k in range(N):
 
            # If already found
            if (found[k]):
                continue
 
            # Evaluate while flow
            # is still in supply
            if (flow[k][src] != 0):
 
                # Obtain the total value
                val = (dist[src] + pi[src] -
                           pi[k] - cost[k][src])
 
                # If dist[k] is > minimum value
                if (dist[k] > val):
 
                    # Update
                    dist[k] = val
                    dad[k] = src
 
            if (flow[src][k] < cap[src][k]):
                val = (dist[src] + pi[src] -
                           pi[k] + cost[src][k])
 
                # If dist[k] is > minimum value
                if (dist[k] > val):
 
                    # Update
                    dist[k] = val
                    dad[k] = src
 
            if (dist[k] < dist[best]):
                best = k
 
        # Update src to best for
        # next iteration
        src = best
 
    for k in range(N):
        pi[k] = min(pi[k] + dist[k], INF)
 
    # Return the value obtained at sink
    return found[sink]
 
# Function to obtain the maximum Flow
def getMaxFlow(capi: List[List[int]],
              costi: List[List[int]],
              src: int, sink: int) -> List[int]:
 
    global cap, cost, found, dist, pi, N, flow, dad
    cap = capi
    cost = costi
 
    N = len(capi)
    found = [False for _ in range(N)]
    flow = [[0 for _ in range(N)]
               for _ in range(N)]
    dist = [INF for _ in range(N + 1)]
    dad = [0 for _ in range(N)]
    pi = [0 for _ in range(N)]
 
    totflow = 0
    totcost = 0
 
    # If a path exist from src to sink
    while (search(src, sink)):
 
        # Set the default amount
        amt = INF
        x = sink
         
        while x != src:
            amt = min(
                amt, flow[x][dad[x]] if
                (flow[x][dad[x]] != 0) else
                  cap[dad[x]][x] - flow[dad[x]][x])
            x = dad[x]
 
        x = sink
         
        while x != src:
            if (flow[x][dad[x]] != 0):
                flow[x][dad[x]] -= amt
                totcost -= amt * cost[x][dad[x]]
 
            else:
                flow[dad[x]][x] += amt
                totcost += amt * cost[dad[x]][x]
                 
            x = dad[x]
 
        totflow += amt
 
    # Return pair total cost and sink
    return [totflow, totcost]
 
# Driver Code
if __name__ == "__main__":
 
    s = 0
    t = 4
 
    cap = [ [ 0, 3, 1, 0, 3 ],
            [ 0, 0, 2, 0, 0 ],
            [ 0, 0, 0, 1, 6 ],
            [ 0, 0, 0, 0, 2 ],
            [ 0, 0, 0, 0, 0 ] ]
 
    cost = [ [ 0, 1, 0, 0, 2 ],
             [ 0, 0, 0, 3, 0 ],
             [ 0, 0, 0, 0, 0 ],
             [ 0, 0, 0, 0, 1 ],
             [ 0, 0, 0, 0, 0 ] ]
 
    ret = getMaxFlow(cap, cost, s, t)
 
    print("{} {}".format(ret[0], ret[1]))
 
# This code is contributed by sanjeev2552


输出:
6 8

时间复杂度: O(V 2 * E 2 )其中V是顶点数,E是边数。
辅助空间: O(V)