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

📅  最后修改于: 2021-09-03 03:18:31             🧑  作者: Mango

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

例子:

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

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

通过向循环中的所有边发送瓶颈容量来处理负循环来解决给定的问题。此外,由于它涉及需求节点,因此调用了 Bellman Ford 算法。

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

  • 将边容量和该边成本存储在两个单独的数组中。
  • 鉴于源节点S和宿节点T,拾取边缘P I,需求节点d节点距离DIST之间,搜索是否有可能以具有选自S流至T。
  • 如果存在流,则计算距离, value = 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)

如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live