Cod sursa(job #2959524)

Utilizator AdelaCorbeanuAdela Corbeanu AdelaCorbeanu Data 31 decembrie 2022 17:16:53
Problema Flux maxim de cost minim Scor 50
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.32 kb
/*
    Dublam nodurile si aplicam algoritmul de cuplaj ce foloseste flux. Graful va arata
    in felul urmator: Nodul de start (0) este legat de nodul X (X <- 1..N) cu capacitatea
    gradului de iesire al lui X (deoarece aceste este numarul de muchii pe care X le primeste
    ca sa le "dea mai departe"); X este legat de toate dublurile nodurilor, cu exceptia
    dublurii lui, adica toate nodurile Y <- 1..N, X != Y, printr-o muchie de cost 1, deoarece
    X poate trimite in Y o singura muchie; Din fiecare dublura avem muchie catre nodul final de
    capacitate egala cu gradul de intrare al nodului respectiv (deoarece atatea muchii au fost duse
    SPRE el).

    Rulam algoritmul de determinare a fluxului maxim si rezultatul va fi cuplajul cautat, adica
    numarul de muchii pe care le-am trasat (egal de asemenea cu jumatate din suma tuturor gradelor).

    Vom considera nodurile initiale (1..N) si sursa (0) ca fiind in primul set, iar dublurile
    si destinatia in al doilea.
    Pentru a obtine muchiile ce alcatuiesc cuplajul, vom afisa muchiile care leaga un nod
    din primul set cu un nod din al doilea set si au capacitate 0 (adica le-am utilizat).
    Pentru reprezentarea grafului, am indexat nodurile astfel:
        -> nod start = 0
        -> nodurile: 1 .. n  (primul set)
        -> dublurile: n + 1 .. 2 * n (al doilea set)
        -> nod final = 2 * n + 1
 */

#include <bits/stdc++.h>
using namespace std;

std::vector<int> bellman_ford (std::vector<std::vector<int>> &nodes_list,
                               std::vector<std::vector<int>> &capacity,
                               std::vector<std::vector<int>> &price) {

    int n = nodes_list.size();
    std::vector<int> distances(n + 1, 1e9);
    std::queue<int> updated;
    std::vector<bool> in_queue(n + 1, false);

    distances[0] = 0;
    updated.push(0);
    in_queue[0] = true;

    while (!updated.empty()) {
        int current = updated.front();
        updated.pop();

        in_queue[current] = false;

        for (auto nbh: nodes_list[current]) {
            if (capacity[current][nbh]) {
                if (distances[current] + price[current][nbh] < distances[nbh]) {
                    distances[nbh] = distances[current] + price[current][nbh];

                    if (!in_queue[nbh]) {
                        updated.push(nbh);
                        in_queue[nbh] = true;
                    }
                }
            }
        }
    }

    return distances;
}

int dijkstra (std::vector<std::vector<int>> &nodes_list,
              std::vector<std::vector<int>> &capacity,
              std::vector<std::vector<int>> &price,
              std::vector<int> &initial_distances,
              std::vector<int> &positive_distances,
              int start, int finish,
              int &flow, int &flow_price) {

    int n = nodes_list.size();
    std::vector<int> dist(n, 1e9);
    std::vector<int> father(n, 0);

    priority_queue<int, vector<int>, greater<>> heap_dist;

    dist[start] = positive_distances[start] = 0;
    heap_dist.push(start);

    while (!heap_dist.empty()) {
        auto current_node = heap_dist.top();
        heap_dist.pop();

        for (auto nbh : nodes_list[current_node]) {
            if (capacity[current_node][nbh]) {

                int new_dist_to_nbh = dist[current_node] + price[current_node][nbh] + initial_distances[current_node] - initial_distances[nbh];
                if (new_dist_to_nbh < dist[nbh]) {
                    dist[nbh] = new_dist_to_nbh;
                    father[nbh] = current_node;
                    positive_distances[nbh] = positive_distances[current_node] + price[current_node][nbh];
                    heap_dist.push(nbh);
                }
            }
        }
    }

    if (dist[finish] == 1e9) return false;

    int path_capacity = 1e9, path_price = 0;
    int current = finish;
    while (current != start) {
        path_capacity = min(path_capacity, capacity[father[current]][current]);
        path_price += price[father[current]][current];
        current = father[current];
    }

    flow += path_capacity;
    flow_price += path_capacity * positive_distances[finish];

    current = finish;
    while (current != start) {
        capacity[current][father[current]] += path_capacity;
        capacity[father[current]][current] -= path_capacity;
        current = father[current];
    }

    return true;
}

int main()
{
    std::ifstream fin("fmcm.in");
    std::ofstream fout("fmcm.out");

    int n, m, start, finish;
    fin >> n >> m >> start >> finish;

    std::vector<std::vector<int>> adj_list(2 * (n + 1));
    std::vector<std::vector<int>> capacity(2 * (n + 1), std::vector<int>(2 * (n + 1), 0));
    std::vector<std::vector<int>> price(2 * (n + 1), std::vector<int>(2 * (n + 1), 0));

    for (int i = 1; i <= m; ++i) {
        int source, dest, cap, cost;
        fin >> source >> dest >> cap >> cost;

        adj_list[source].push_back(dest);
        adj_list[dest].push_back(source);
        capacity[source][dest] = cap;
        price[source][dest] = cost;
        price[dest][source] = -cost;
    }

    auto initial_distances = bellman_ford(adj_list, capacity, price);
    std::vector<int> positive_distances(n + 1, 1e9);

    int flow = 0, flow_price = 0;

    while (dijkstra(adj_list, capacity, price, initial_distances, positive_distances, start, finish, flow, flow_price));

    fout << flow_price;

    return 0;
}