Алгоритм Диника для максимального потока с нецелочисленными ребрами - PullRequest
0 голосов
/ 29 октября 2018

Существует реализация C ++ алгоритма Dinic для задачи максимального потока, которую я пытался использовать. В этом коде C ++ предполагается, что все параметры являются целочисленными. Я попытался преобразовать код в эквивалентный код, в котором емкость ребер может быть непрерывной (нецелой). Вот мой код:

// 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; 
} 

В этом коде я изменил тип flow и C на double. Я также изменил некоторые части кода, чтобы адаптировать его к этому новому типу double. Код работает только изредка! Он либо выводит Maximum flow 23, который является правильным выводом, либо выдает ошибку Segmentation fault (core dumped). Я действительно не знаю, что не так в этом коде. Есть идеи?

1 Ответ

0 голосов
/ 29 октября 2018

Я не знаю, правильный ли алгоритм, но при условии, что код ссылки содержит несколько проблем.

  1. Использование заголовка #include<bits/stdc++.h>.
  2. Утечки памяти.

Во-первых, следует избегать использования bits / stdc ++. H и использовать надлежащие #include файлы.

Во-вторых, использование std::vector устранит утечки памяти. Код использует std::vector в некоторых местах, но полностью отказывается использовать его в других местах. Например:

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

следует заменить на:

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

Первая вызывала утечку памяти из-за отсутствия delete [] start; в коде. При использовании std::vector утечка памяти исчезает.

Как только введено std::vector, в классе Graph, который отслеживает количество вершин, не требуется переменная-член V. Причина в том, что члены vector имеют размер V вершин, а vector уже знает свой собственный размер с помощью функции-члена vector::size(). Таким образом, переменная-член V является избыточной и может быть удалена.

Последнее изменение, которое можно сделать, - это переместиться на struct Edge в класс Graph.


Учитывая все упомянутые изменения, здесь приведена очищенная версия, которая возвращает тот же результат, что и исходный код, при запуске с графиком теста, установленным в функции 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;
}

Функция проверки выглядит следующим образом:

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); 
}

Живой пример


Теперь, если вы хотите посмотреть, что произойдет, если вы используете double вместо int в качестве величины потока, проще всего создать класс шаблона на основе приведенного выше кода. Цель состоит в том, чтобы взять, где используется int, и вместо замены int на double заменить int параметром шаблона (например, T). Полученный код будет выглядеть следующим образом:

#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;
}

Живой пример

Тестовый пример main будет состоять в том, чтобы просто использовать Graph<double> или Graph<int> вместо простого Graph - все остальное в функции main остается прежним.

Теперь, когда функция является шаблоном, любой числовой тип, который поддерживает те же операции, что и int или double, можно легко заменить, просто создав Graph<numerical_type>.

...