Cod sursa(job #2958839)

Utilizator mihairazvan03Dana Mihai mihairazvan03 Data 28 decembrie 2022 16:13:35
Problema Flux maxim de cost minim Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.78 kb
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <climits>

//facem ca la problema cu flux doar ca adaugam niste modificari. pt fiecare arc, pe langa capacitate retinem si costul, iar pt arcele inverse retinem costul cu minus si capacitate 0. Incercam
//cat timp se mai poate adauga flux, si de fiecare data incercam pt capacitatea respectiva maxima de flux sa gasim un drum de cost minim. Folosim Bellman Ford pt a calcula vCost, adica costul
//minim de la sursa la un nod (facem cu bellman pt ca avem si costuri negative). Apoi pentru a putea aplica dijkstra trebuia sa facem aceste costuri pozitive, asa ca pt fiecare arc dintre 2 noduro
//nod1 si nod2 vCost[arc] = vCostArc[nod1][nod2] + vCost[nod1] - vCost[nod2]
//Complexitatea de timp O(n * (m ^2) * log n), unde n = nr noduri, m = nr arce. Complex spatiu O(n ^ 2)

using namespace std;
ifstream in("fmcm.in");
ofstream out("fmcm.out");

int n, m, sursa, destinatie, costMin;
vector<vector<int>> adj;
vector<vector<int>> vCapacitate;      //matricea de capacitati (ce flux avem intre 2 noduri)
vector<vector<int>> vCostArc;   //costul unui arc dintre 2 noduri (0 daca nu avem muchie). Pt arcele inverse trecem costul dat cu minus
vector<int> vCostInit;
vector<int> vCost;      //costul minim la la sursa la nodul respectiv
vector<int> vCostActual;


bool Dijkstra()
{
    int i, currenNode, currenNodeDist, j, newNode, newCost, capacitateMin = INT_MAX, newDest = destinatie, costActual = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    vector<int> dad;     //folosim pt a retine drumul care se formeaza. Incepem de la destinatie dupa mergem la dad, care e nodul de dinainte dest, si tot asa pana ajungem la sursa
    dad.clear();   //trb sa stergem ce era in dijkstrau trecut. daca am da doar resize elementele s-ar pastra, nu s-ar reseta
    dad.resize(n + 1);

    for(i = 1; i <= n; i++)
        vCost[i] = INT_MAX;

    vCost[sursa] = 0;
    vCostActual[sursa] = 0;
    pq.push(make_pair(0, sursa));

    while(!pq.empty())
    {
        currenNode = pq.top().second;
        currenNodeDist = pq.top().first;
        pq.pop();

        if(vCost[currenNode] == currenNodeDist)
            for(j = 0; j < adj[currenNode].size(); j++)
            {
                newNode = adj[currenNode][j];

                if(vCapacitate[currenNode][newNode] > 0)   //mai avem capacitate de flux ramasa
                {
                    newCost = vCost[currenNode] + vCostArc[currenNode][newNode] + vCostInit[currenNode] - vCostInit[newNode];

                    if (newCost < vCost[newNode])
                    {
                        vCost[newNode] = newCost;
                        vCostActual[newNode] = vCostActual[currenNode] + vCostArc[currenNode][newNode];
                        dad[newNode] = currenNode;
                        pq.push(make_pair(vCost[newNode], newNode));
                    }
                }
            }
    }

    for(i = 1; i <= n; i++)
        vCostInit[i] = vCostActual[i];

    if(vCost[destinatie] == INT_MAX)       //inseamna ca nu am reusit sa ajungem la destinatie
        return false;

    while(newDest != sursa)
    {
        capacitateMin = min(capacitateMin, vCapacitate[dad[newDest]][newDest]);
        costActual += vCostArc[dad[newDest]][newDest];
        newDest = dad[newDest];
    }

    costMin += capacitateMin * vCostActual[destinatie];
    newDest = destinatie;

    while(newDest != sursa)
    {
        vCapacitate[dad[newDest]][newDest] -= capacitateMin;
        vCapacitate[newDest][dad[newDest]] += capacitateMin;    //crestem capacitatea ramasa la arcele inverse
        newDest = dad[newDest];
    }

    return true;
}


void BellmanFord()
{
    int i, currenNode, j, newNode;
    queue<int> coada;
    vector<int> inCoada(n + 1, 0);   //daca nodul se afla in coada

    for(i = 1; i <= n; i++)
        vCostInit[i] = INT_MAX;

    vCostInit[sursa] = 0;
    coada.push(sursa);
    inCoada[sursa] = 1;

    while (!coada.empty())
    {
        currenNode = coada.front();
        coada.pop();
        inCoada[currenNode] = 0;     //nodul nu mai e in coada pt ca i-am dat pop

        for(j = 0; j < adj[currenNode].size(); j++)
        {
            newNode = adj[currenNode][j];

            if(vCapacitate[currenNode][newNode])
                if(vCostInit[currenNode] + vCostArc[currenNode][newNode] < vCostInit[newNode])  //e mai ieftin sa ajungem la newNode trecand prin currentNode
                {
                    vCostInit[newNode] = vCostInit[currenNode] + vCostArc[currenNode][newNode];

                    if (inCoada[newNode] == 0)     //daca nu e deja in coada il bagam
                    {
                        inCoada[newNode] = 1;
                        coada.push(newNode);
                    }
                }
        }
    }
}


int main()
{
    int i, nod1, nod2, capacitate, cost;

    in >> n >> m >> sursa >> destinatie;
    adj.resize(n + 1);
    vCapacitate.resize(n + 1);
    vCostArc.resize(n + 1);
    vCost.resize(n + 1, 0);
    vCostActual.resize(n + 1, 0);
    vCostInit.resize(n + 1, 0);
    for(i = 0; i <= n; i++)
    {
        vCapacitate[i].resize(n + 1, 0);
        vCostArc[i].resize(n + 1, 0);
    }

    for (int i = 0; i < m; i++)
    {
        in >> nod1 >> nod2 >> capacitate >> cost;
        adj[nod1].push_back(nod2);
        adj[nod2].push_back(nod1);
        vCapacitate[nod1][nod2] = capacitate;
        vCostArc[nod1][nod2] = cost;
        vCostArc[nod2][nod1] = -cost;    //costul pt arcul invers este minusul costului arcului
    }

    BellmanFord();

    for(;;)    //facem dijkstra cat se poate
        if(Dijkstra() == false)
        break;

    out << costMin;

    return 0;
}