简体   繁体   English

具有非整数边缘容量的Dinic最大流量算法

[英]Dinic’s algorithm for Maximum Flow with non-integer edge capacities

There is a C++ implementation of Dinic's algorithm for Maximum Flow Problem that I was trying to use. 我尝试使用Dinic算法的C ++实现来解决最大流量问题。 In that C++ code, it is assumed that all parameters are integer. 在该C ++代码中,假定所有参数都是整数。 I tryied to convert the code to an equivalent one in which capacity of edges can be continuous (non-integer). 我尝试将代码转换为等效的代码,其中边的容量可以是连续的(非整数)。 Here is my code: 这是我的代码:

// C++ implementation of Dinic's Algorithm 
// #include "HEADERS.h"

#include<bits/stdc++.h> 



using namespace std; 

double EPSILON = 1e-6 ;
// A structure to represent a edge between 
// two vertex 
struct Edge 
{ 
    int v ; // Vertex v (or "to" vertex) 
            // of a directed edge u-v. "From" 
            // vertex u can be obtained using 
            // index in adjacent array. 

    double flow ; // flow of data in edge 

    double C; // capacity 

    int rev ; // To store index of reverse 
            // edge in adjacency list so that 
            // we can quickly find it. 
}; 

// Residual Graph 
class Graph 
{ 
    int V; // number of vertex 
    int *level ; // stores level of a node 
    vector< Edge > *adj; 
public : 
    Graph(int V) 
    { 
        adj = new vector<Edge>[V]; 
        this->V = V; 
        level = new int[V]; 
    } 

    // add edge to the graph 
    void addEdge(int u, int v, double C) 
    { 
        // Forward edge : 0 flow and C capacity 
        Edge a{v, 0, C, static_cast<int> (adj[v].size()) }; 

        // Back edge : 0 flow and 0 capacity 
        Edge b{u, 0, 0, static_cast<int> (adj[u].size()) }; 

        adj[u].push_back(a); 
        adj[v].push_back(b); // reverse edge 
    } 

    bool BFS(int s, int t); 
    int sendFlow(int s, double flow, int t, int ptr[]); 
    double DinicMaxflow(int s, int t); 
}; 

// Finds if more flow can be sent from s to t. 
// Also assigns levels to nodes. 
bool Graph::BFS(int s, int t) 
{ 
    for (int i = 0 ; i < V ; i++) 
        level[i] = -1; 

    level[s] = 0; // Level of source vertex 

    // Create a queue, enqueue source vertex 
    // and mark source vertex as visited here 
    // level[] array works as visited array also. 
    list< int > q; 
    q.push_back(s); 

    vector<Edge>::iterator i ; 
    while (!q.empty()) 
    { 
        int u = q.front(); 
        q.pop_front(); 
        for (i = adj[u].begin(); i != adj[u].end(); i++) 
        { 
            Edge &e = *i; 
            if (level[e.v] < 0 && e.flow < e.C) 
            { 
                // Level of current vertex is, 
                // level of parent + 1 
                level[e.v] = level[u] + 1; 

                q.push_back(e.v); 
            } 
        } 
    } 

    // IF we can not reach to the sink we 
    // return false else true 
    return level[t] < 0 ? false : true ; 
} 

// A DFS based function to send flow after BFS has 
// figured out that there is a possible flow and 
// constructed levels. This function called multiple 
// times for a single call of BFS. 
// flow : Current flow send by parent function call 
// start[] : To keep track of next edge to be explored. 
//       start[i] stores count of edges explored 
//       from i. 
// u : Current vertex 
// t : Sink 
int Graph::sendFlow(int u, double flow, int t, int start[]) 
{ 
    // Sink reached 
    if (u == t) 
        return flow; 

    // Traverse all adjacent edges one -by - one. 
    for ( ; start[u] < static_cast <int> (adj[u].size()) ; start[u]++) 
    { 
        // Pick next edge from adjacency list of u 
        Edge &e = adj[u][start[u]]; 

        if (level[e.v] == level[u]+1 && e.flow < e.C) 
        { 
            // find minimum flow from u to t 
            double curr_flow = min(flow, e.C - e.flow); 

            double temp_flow = sendFlow(e.v, curr_flow, t, start); 

            // flow is greater than zero 
            if (temp_flow > 0) 
            { 
                // add flow to current edge 
                e.flow += temp_flow; 

                // subtract flow from reverse edge 
                // of current edge 
                adj[e.v][e.rev].flow -= temp_flow; 
                return temp_flow; 
            } 
        } 
    } 

    return 0; 
} 
// Returns maximum flow in graph 
double Graph::DinicMaxflow(int s, int t) 
{ 
    // Corner case 
    if (s == t) 
        return -1; 

    double total = 0; // Initialize result 

    // Augment the flow while there is path 
    // from source to sink 
    while (BFS(s, t) == true) 
    { 
        // store how many edges are visited 
        // from V { 0 to V } 
        int *start = new int[V+1]; 
        double flow = sendFlow(s, INT_MAX, t, start) ;
        // while flow is not zero in graph from S to D 
        while (flow > EPSILON )
        {
            // Add path flow to overall flow 
            total += flow;
            flow = sendFlow(s, INT_MAX, t, start) ;
        }
    } 

    // return maximum flow 
    return total; 
} 

// Driver program to test above functions 
int main() 
{ 
    Graph g(6); 
    g.addEdge(0, 1, 16 ); 
    g.addEdge(0, 2, 13 ); 
    g.addEdge(1, 2, 10 ); 
    g.addEdge(1, 3, 12 ); 
    g.addEdge(2, 1, 4 ); 
    g.addEdge(2, 4, 14); 
    g.addEdge(3, 2, 9 ); 
    g.addEdge(3, 5, 20 ); 
    g.addEdge(4, 3, 7 ); 
    g.addEdge(4, 5, 4); 

    // next exmp 
    /*g.addEdge(0, 1, 3 ); 
    g.addEdge(0, 2, 7 ) ; 
    g.addEdge(1, 3, 9); 
    g.addEdge(1, 4, 9 ); 
    g.addEdge(2, 1, 9 ); 
    g.addEdge(2, 4, 9); 
    g.addEdge(2, 5, 4); 
    g.addEdge(3, 5, 3); 
    g.addEdge(4, 5, 7 ); 
    g.addEdge(0, 4, 10); 

    // next exp 
    g.addEdge(0, 1, 10); 
    g.addEdge(0, 2, 10); 
    g.addEdge(1, 3, 4 ); 
    g.addEdge(1, 4, 8 ); 
    g.addEdge(1, 2, 2 ); 
    g.addEdge(2, 4, 9 ); 
    g.addEdge(3, 5, 10 ); 
    g.addEdge(4, 3, 6 ); 
    g.addEdge(4, 5, 10 ); */
    cout << "Maximum flow " << g.DinicMaxflow(0, 5) << endl ; 
    return 0; 
} 

In this code, I have changed type of flow and C to double . 在这段代码中,我将flowC类型更改为double I also changed some parts of the code to adapt it to this new double type. 我还更改了代码的某些部分,以使其适应这种新的double类型。 The code works only occusionally! 该代码仅在某些情况下起作用! It either outputs Maximum flow 23 which is the right output or throws a Segmentation fault (core dumped) error. 它要么输出Maximum flow 23它是正确的输出),要么抛出Segmentation fault (core dumped)错误。 I don't really know what is wrong in this code. 我真的不知道这段代码有什么问题。 Any ideas? 有任何想法吗?

I don't know if the algorithm is correct, but assuming it is, the code at the link has several issues. 我不知道该算法是否正确,但是假设是正确的,那么链接上的代码有几个问题。

  1. Usage of the #include<bits/stdc++.h> header. #include<bits/stdc++.h>标头的用法。
  2. Memory leaks. 内存泄漏。

First, usage of bits/stdc++.h should be avoided , and the proper #include files should be used. 首先, 应避免使用bits / stdc ++。h ,并应使用正确的#include文件。

Second, the usage of std::vector would take care of the memory leaks. 其次,使用std::vector可以解决内存泄漏问题。 The code uses std::vector in some places, but totally refuses to use it in other places. 该代码在某些地方使用std::vector ,但完全拒绝在其他地方使用它。 For example: 例如:

int *start = new int[V+1];

should be replaced with: 应替换为:

std::vector<int> start(V+1);

The former was causing a memory leak due to the lack of a delete [] start; 前者由于缺少delete [] start;而导致内存泄漏delete [] start; in the code. 在代码中。 Using std::vector , the memory leak disappears. 使用std::vector ,内存泄漏消失。

Once std::vector is introduced, there is no need for the V member variable in the Graph class that tracks the number of vertices. 一旦引入了std::vector ,就无需在Graph类中使用V成员变量来跟踪顶点数量。 The reason why is that the vector members are sized to V vertices, and a vector already knows their own size by using the vector::size() member function. 原因是vector成员的大小为V个顶点,并且vector已经通过使用vector::size()成员函数知道了自己的大小。 So the V member variable is redundant and can be removed. 因此, V成员变量是多余的,可以删除。

The last change that can be made is to move struct Edge within the Graph class. 可以做的最后更改是在Graph类中移动struct Edge


Given all of the changes mentioned, here is a cleaned up version that returns the same result as the original code when run with the test graph set up in the main() function: 考虑到上述所有更改,下面是一个清理后的版本,当使用在main()函数中设置的测试图运行时,该返回的结果与原始代码相同:

#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <climits>

class Graph
{
    struct Edge
    {
        int v; 
        int flow; 
        int C; 
        int rev; 
    };

    std::vector<int> level;
    std::vector<std::vector< Edge >> adj;

public:
    Graph(int V) : level(V), adj(V) {}
    void addEdge(int u, int v, int C)
    {
        adj[u].push_back({ v, 0, C, static_cast<int>(adj[v].size()) });
        adj[v].push_back({ u, 0, 0, static_cast<int>(adj[u].size()) }); 
    }

    bool BFS(int s, int t);
    int sendFlow(int s, int flow, int t, std::vector<int>& ptr);
    int DinicMaxflow(int s, int t);
};

bool Graph::BFS(int s, int t)
{
    std::fill(level.begin(), level.end(), -1);
    level[s] = 0; 
    std::list< int > q;
    q.push_back(s);
    std::vector<Edge>::iterator i;
    while (!q.empty())
    {
        int u = q.front();
        q.pop_front();
        for (i = adj[u].begin(); i != adj[u].end(); i++)
        {
            Edge &e = *i;
            if (level[e.v] < 0 && e.flow < e.C)
            {
                level[e.v] = level[u] + 1;
                q.push_back(e.v);
            }
        }
    }
    return level[t] < 0 ? false : true;
}

int Graph::sendFlow(int u, int flow, int t, std::vector<int>& start)
{
    if (u == t)
        return flow;
    for (; start[u] < static_cast<int>(adj[u].size()); start[u]++)
    {
        // Pick next edge from adjacency list of u 
        Edge &e = adj[u][start[u]];

        if (level[e.v] == level[u] + 1 && e.flow < e.C)
        {
            int curr_flow = std::min(flow, e.C - e.flow);
            int temp_flow = sendFlow(e.v, curr_flow, t, start);

            if (temp_flow > 0)
            {
                e.flow += temp_flow;
                adj[e.v][e.rev].flow -= temp_flow;
                return temp_flow;
            }
        }
    }
    return 0;
}

int Graph::DinicMaxflow(int s, int t)
{
    if (s == t)
        return -1;
    int total = 0; 
    while (BFS(s, t) == true)
    {
        std::vector<int> start(level.size() + 1);
        while (int flow = sendFlow(s, INT_MAX, t, start))
            total += flow;
    }
    return total;
}

The test function is as follows: 测试功能如下:

int main() 
{ 
    Graph g(6); 
    g.addEdge(0, 1, 16 ); 
    g.addEdge(0, 2, 13 ); 
    g.addEdge(1, 2, 10 ); 
    g.addEdge(1, 3, 12 ); 
    g.addEdge(2, 1, 4 ); 
    g.addEdge(2, 4, 14); 
    g.addEdge(3, 2, 9 ); 
    g.addEdge(3, 5, 20 ); 
    g.addEdge(4, 3, 7 ); 
    g.addEdge(4, 5, 4); 
    std::cout << "Maximum flow " << g.DinicMaxflow(0, 5); 
}

Live Example 现场例子


Now, if you want to see what happens if you use double instead of int as the flow amount, the easiest thing to do is to create a template class based on the code above. 现在,如果要查看使用double而不是int作为流量会发生什么,最简单的方法是根据上述代码创建模板类。 The goal is to take where int is used and instead of replacing int with double , replace int with a template parameter (for example, T ). 我们的目标是采取其中int被使用并且而不是替换intdouble ,替换int与模板参数(例如, T )。 The resulting code would be as follows: 结果代码如下:

#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <climits>

template <typename T>
class Graph
{
    struct Edge
    {
        int v;
        T flow;
        T C;
        int rev;
    };

    std::vector<int> level;
    std::vector<std::vector<Edge>> adj;

public:
    Graph(int V) : level(V), adj(V) {}
    void addEdge(int u, int v, T C)
    {
        adj[u].push_back({ v, T(), C, static_cast<int>(adj[v].size())});
        adj[v].push_back({ u, T(), T(), static_cast<int>(adj[u].size())}); // reverse edge 
    }
    bool BFS(int s, int t);
    T sendFlow(int s, T flow, int t, std::vector<int>& ptr);
    T DinicMaxflow(int s, int t);
};

template <typename T>
bool Graph<T>::BFS(int s, int t)
{
    std::fill(level.begin(), level.end(), -1);
    level[s] = 0; 
    std::list< int > q;
    q.push_back(s);

    typename std::vector<Edge>::iterator i;
    while (!q.empty())
    {
        int u = q.front();
        q.pop_front();
        for (i = adj[u].begin(); i != adj[u].end(); i++)
        {
            Edge &e = *i;
            if (level[e.v] < 0 && e.flow < e.C)
            {
                level[e.v] = level[u] + 1;
                q.push_back(e.v);
            }
        }
    }
    return level[t] < 0 ? false : true;
}

template <typename T>
T Graph<T>::sendFlow(int u, T flow, int t, std::vector<int>& start)
{
    if (u == t)
        return flow;

    for (; start[u] < static_cast<int>(adj[u].size()); start[u]++)
    {
        Edge &e = adj[u][start[u]];
        if (level[e.v] == level[u] + 1 && e.flow < e.C)
        {
            T curr_flow = std::min(flow, e.C - e.flow);
            T temp_flow = sendFlow(e.v, curr_flow, t, start);
            if (temp_flow > 0)
            {
                e.flow += temp_flow;
                adj[e.v][e.rev].flow -= temp_flow;
                return temp_flow;
            }
        }
    }
    return 0;
}

template <typename T>
T Graph<T>::DinicMaxflow(int s, int t)
{
    if (s == t)
        return -1;
    T total = 0; 

    while (BFS(s, t) == true)
    {
        std::vector<int> start(level.size() + 1);
        while (T flow = sendFlow(s, INT_MAX, t, start))
            total += flow;
    }
    return total;
}

Live Example 现场例子

The main test example would be to simply use Graph<double> or Graph<int> instead of simply Graph -- everything else in the main function stays the same. main测试示例将是简单地使用Graph<double>Graph<int>而不是简单地使用Graphmain函数中的所有其他内容保持不变。

Now that the function is a template, any numerical type that supports the same operations as int or double can be substituted easily by just creating a Graph<numerical_type> . 现在,该函数是模板,只需创建Graph<numerical_type>即可轻松替换支持与intdouble相同的任何数字类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM