Cod sursa(job #3217077)

Utilizator adiadi11Adrian Ariton adiadi11 Data 20 martie 2024 22:18:40
Problema Flux maxim de cost minim Scor 20
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 6.46 kb
#include <iostream>
#include <fstream>

#include <vector>
#include <queue>
#define REVERSE 1
#define NORMAL 0
std::ifstream fin("fmcm.in");
std::ofstream fout("fmcm.out");
class Edge;
class Graph;


class Edge {
public:
    long long source;
    long long dest;
    long long cap;
    long long flow;
    long long type;
    long long cost;
    Edge *reverse;
    Edge(){
        type = 0;
        source = 0;
        dest = 0;
        cap = 0;
        flow = 0;
        cost = 0;
    }
    Edge(long long t=0) : type(t) {
        source = 0;
        dest = 0;
        cap = 0;
        flow = 0;
        cost = 0;
    }
    void point(Edge *rev) {
        this->reverse = rev;
    }
    Edge(long long s, long long d, long long c, long long co, long long t = 0) : source(s), dest(d), cap(c), flow(0), type(t), cost(co) {}

};

class Graph {
public:
    long long nodes;
    std::vector< std::vector<Edge *> > graph;
    std::vector<Edge *> all_edges;
    std::vector<Edge *> all_revedges;

    Graph(int n) : nodes(n) {
        graph.reserve(n+1);
    }

    void addEdge(long long s, long long d, long long c, long long co) {
        Edge *e1 = new Edge(s, d, c, co);
        Edge *e2 = new Edge(d, s, 0, -co, REVERSE);
        e1->point(e2);
        e2->point(e1);
        graph[s].push_back(e1);
        graph[d].push_back(e2);
        all_edges.push_back(e1);
        all_revedges.push_back(e2);
    }
};

namespace BellmanFord {
    std::vector<int> bellmanford(const Graph& g, int source) {
        std::vector <int> dist(g.nodes + 1, 1e9);
        std::vector <int> pred(g.nodes + 1, -1);
        dist[source] = 0;
        for (int i = 0; i < g.nodes - 1; ++i) {
            for (auto edge : g.all_edges) {
                if (dist[edge->source] != 1e9 && dist[edge->source] + edge->cost < dist[edge->dest]) {
                    dist[edge->dest] = dist[edge->source] + edge->cost;
                    pred[edge->dest] = edge->source;
                }
            }
            
        }

        for (auto edge : g.all_edges) {
            if (dist[edge->source] != 1e9 && dist[edge->source] + edge->cost < dist[edge->dest]) {
                std::cout << "Graph contains negative cicle\n";
            }
        }
        return dist;
    }

    void raise(Graph* g, std::vector<int> distances) {
        // for (int i = 1; i <= g->nodes; ++i) {
        //     std::cout << "\t" << "distance from source to " << i << ": " << distances[i] <<"\n"; 
        // }
        for (auto& edge : g->all_edges) {
            edge->cost += distances[edge->source] - distances[edge->dest];
        }

        for (auto& edge : g->all_revedges) {
            edge->cost += distances[edge->source] - distances[edge->dest];
        }
    }

    void lower(Graph* g, std::vector<int> distances) {
        for (auto& edge : g->all_edges) {
            edge->cost -= distances[edge->source] - distances[edge->dest];
        }
        for (auto& edge : g->all_revedges) {
            edge->cost -= distances[edge->source] - distances[edge->dest];
        }
    }
}

namespace Dijkstra {
    class EdgeEntryComparator {
    public:
        std::vector<int>& cost;
        EdgeEntryComparator(std::vector<int>& _cost) : cost(_cost) {}
        bool operator()(int a,  int b) {
            return this->cost[a] > this->cost[b];
        }
    };
    
    template <typename T = EdgeEntryComparator>
    std::pair< std::vector<int>, std::vector<Edge *> > dijkstra_bef(const Graph& g, int source, std::vector<int> distances) {
        std::vector<int> dist(g.nodes + 1, 1e9);
        std::vector<int> real_dist(g.nodes + 1, 1e9);

        T comp = T(dist);
        std::priority_queue<int, std::vector<int>, T> pq(comp);
        std::vector<bool> isinqueue(g.nodes + 1, false);
        std::vector<Edge *> bef(g.nodes + 1, nullptr);
        dist[source] = 0;
        real_dist[source] = 0;
        pq.push(source);
        isinqueue[source] = true;
        while(!pq.empty()) {
            int node = pq.top();
            pq.pop();
            isinqueue[node] = false;
            for (auto& edge : g.graph[node]) {
                int neigh = edge->dest;
                // std:: cout << node << " -> " << neigh << " curent cost: " <<  dist.at(neigh) << "\n";
                if (edge->cap) {
                    if (neigh != node && dist.at(neigh) > dist.at(node) + edge->cost
                        && edge->flow < edge->cap) { // added bc of flow
                        // std:: cout << "Adding: " << node << " -> " << neigh << " :: " << edge->cost << "\n";
                        dist.at(neigh) = dist.at(node) + edge->cost;
                        real_dist.at(neigh) = real_dist.at(node) + edge->cost - (distances[edge->source] - distances[edge->dest]);
                        if (isinqueue[neigh] == false) {
                            pq.push(neigh);
                            isinqueue[neigh] = true;
                        }
                        bef[neigh] = edge;
                    }
                }
            }
        }
        for (auto& x : dist) {
            if (x == 1e9)
                x = -1;
        }
        return std::make_pair(real_dist, bef);
    }
}

long long max_flow_min_cost(Graph& g, long long source, long long dest) {
    int total_flow = 0;
    int flow_cost = 0;
    auto distances_bf = BellmanFord::bellmanford(g, source);
    BellmanFord::raise(&g, distances_bf);
    while (1) {
        auto dist_pred = Dijkstra::dijkstra_bef(g, source, distances_bf);

        std::vector<Edge *> predec = dist_pred.second;
        std::vector<int> dists = dist_pred.first;
    
        if (predec[dest] == nullptr)
            break;

        long long flow = 110001;
        for (Edge *edge = predec[dest]; edge != nullptr; edge = predec[edge->source]) {
            flow = std::min(flow, edge->cap - edge->flow);
        }

        if (flow == 0)
            break;
        total_flow += flow;
        flow_cost += flow * dists[dest];
        for (Edge *edge = predec[dest]; edge != nullptr; edge = predec[edge->source]) {
            edge->flow = edge->flow + flow;
            edge->reverse->flow = edge->reverse->flow - flow;
        }
    }
    BellmanFord::lower(&g, distances_bf);
    return flow_cost;
    // long long x = 0;
    // for (auto ed : graph[source]) {
    //     x += ed->flow;
    // }
    // return x;
}

int main() {
    int n, m, so = 1, de;
    fin >> n >> m >> so >> de;
    Graph g(n);
    int s, d, c, co;

    for (int i = 0; i < m; ++i) {
        fin >> s >> d >> c >> co;
        g.addEdge(s, d, c, co);
    }

    fout << max_flow_min_cost(g, so, de);
}