Cod sursa(job #2962379)

Utilizator AncaGAncuta Gava AncaG Data 8 ianuarie 2023 14:45:12
Problema Cuplaj maxim in graf bipartit Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 6.3 kb

#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <algorithm>
#include <climits>

using namespace std;

// ifstream fin("input.txt");
// ofstream fout("output.txt");




// Cum am gandit:
// Din suportul de curs avem o serie de pasi pentru determinarea cuplajul maxim
// cuplajul maxim fiind in esenta nr maxim de muchii (un varf din left si un varf din right) care nu se intersecteaza/suprapun la capete
// a relatied de 1 la 1 fara suprapunere

// il putem afla  prin flux maxim adaugand o sursa si o destinatie la multimile left si right de noduri
// adaugam muchiile din sursa si destinatie in relatie cu cele doua multimi
// iar fiecare muchie are capacitatea 1
// aplicam alg de flux maxim Ford

// fluxul maxim va da cardinalitatea cuplajului maxim
// iar muchiile saturate din "miez" (left si right cu propr de cuplaj maxim) var fi muchiile ce tr afisate
// am aplicat logica din suportul de curs pe Ford cu updatare de fluxuri

struct Edge
{
    int sursa, destinatie, capacitate, pozitie; // pozitia ma ajuta sa accesez muchia ulterior
};

int n, m, e, s, d;
int nr_noduri;
vector<vector<int>> adj_indici; //in lista de adiacenta o sa pastram indexul muchiei
vector<Edge> edges;

void addEdge (int a, int b)
{
    // In lista de adiacenta o sa pastram indexul muchiei
    int index = (int) edges.size();

    // muchiile create de capacitate 1
    Edge m_1 = {a, b, 1, index + 1};

    //lista de adiacentta a muchiilor sub forma de indici
    adj_indici[b].push_back(index + 1);
    edges.push_back(m_1);

    //muchiile inverse ce vor fi folosite la graful rezidual, atentie sa pun capacitate 0 aici
    Edge m_2 = {b, a, 0, index};
    adj_indici[a].push_back(index);
    edges.push_back(m_2);
}

// Aplicam BFS pt a determina daca exista un path de la sursa la destinatie,
// cu focus pe acele drumuri cu capc reziduala >0 (in acest sens mai putem adauga flux)

vector<int> parinte;
vector<int> vizitat;

bool bfs_cuplaj()
{
    // puteam si cu     memset(vazut, 0, sizeof(vazut));
    parinte.clear();
    parinte.resize(nr_noduri);

    vizitat.clear();
    vizitat.resize(nr_noduri, 0);

    // Vom porni intotdeauna de la sursa
    vizitat[s] = 1;
    // Coada in care vom retine nodurile
    queue<int> q_bfs;
    q_bfs.push(s); // coada specifica BFS-ului

    // parcurg pentru gasirea de path cu ajutorul cozii de bfs
    while (!q_bfs.empty())
    {
        int nod = q_bfs.front();
        q_bfs.pop();
        if (nod != d) // daca nu am atins destinatia
            for (auto index_muchie: adj_indici[nod])
            {

                Edge ed_curent = edges[index_muchie]; // // accesez informatiile legate de acea muchie cu ajutorul structurii
 // nevizitare a nodului destinatie din muchia in cauza si nesaturare pe ea pt a coontinua pe acest path
                if (!vizitat[ed_curent.destinatie] && ed_curent.capacitate > 0)
                {
                    vizitat[ed_curent.destinatie] = 1; // este vazut si marcat
                    parinte[ed_curent.destinatie] = index_muchie; // preiau parintele ca pozitie
                    q_bfs.push(ed_curent.destinatie); // adaugam nodul la coada bfs
                }
            }
    }
    return vizitat[d]; // mi-a gasit path pe care sa fac update de flux
}


int fluxMaxim ()
{
    int flux_max = 0;
    // cat timp mai avem un drum de la sursa la destinatie
    while (bfs_cuplaj())
    {
        for (auto index_muchie: adj_indici[d])
        {
            // verific capacitatea si destinatia la nivel de muchie
            // daca este nesaturata o preiau mai departe si actualizez parintele destinatiei
            // verific in esenta de la o destinatie la sursa, in sens invers

            if (vizitat[edges[index_muchie].destinatie] &&
                        edges[edges[index_muchie].pozitie].capacitate > 0)
            {
                int flux = INT_MAX;
                Edge ed_curent = edges[index_muchie]; // merg de la edge-ul curent si asociez parintii prin index
                parinte[d] = ed_curent.pozitie;

                int nod = d;
                while (nod != s)
                {
                    int prev = parinte[nod];
                    // partea de aflarea a minimului folosit pt augumentare
                    flux = min(flux, edges[prev].capacitate);
                    nod = edges[prev].sursa;
                }
                // partea de actualizare a fluxului per muchii
                nod = d;
                // si refac fluxul atata timp cat nu am ajuns la sursa pe acel path
                while (nod != s)
                {
                    int prev = parinte[nod];
                    edges[edges[prev].pozitie].capacitate += flux;
                    edges[prev].capacitate -= flux;
                    nod = edges[prev].sursa;
                }
                // fluxul maxim calculat
                flux_max += flux;
            }
        }
    }
    return flux_max;
}

int main () {

    ifstream f_cin("cuplaj.in");
    ofstream f_cout("cuplaj.out");

    f_cin >> n >> m >> e;
    // Calculam numarul total de noduri
    nr_noduri = n + m + 2;
    // Construim sursa si destinatia
    // Stim ca nodurile incep de la 0 si sunt consecutive
    s = 0;
    d = nr_noduri - 1;
    // Construim graful
    adj_indici.resize(nr_noduri);

    // Citim muchiile
    for (int i = 0; i < e; i++) {
        int x, y;
        f_cin >> x >> y;
        // Adaugam muchia in lista de adiacenta
        addEdge(x, y + n);
    }

    // Adaugam muchiile de la sursa la nodurile din multimea A
    for (int i = 1; i <= n; ++i)
        addEdge(s, i);

    // Adaugam muchiile de la nodurile din multimea B la destinatie
    for (int i = 1; i <= m; ++i)
        addEdge(i + n, d);

    // Aplicam Ford-Fulkerson
    f_cout << fluxMaxim() << endl;

    // conditia de cuplaj maxim  (evit muchiile in nodul start/destinatie artificiale)
    // pt afisarea muchiilor saturate din "miez" (left si right)
    for (Edge muchie: edges) {
        if (muchie.sursa < muchie.destinatie && muchie.sursa != 0 && muchie.destinatie != d && muchie.capacitate == 0)
            f_cout << muchie.sursa << " " << muchie.destinatie - n <<endl; // sa nu uit sa rescaluez la val muchiei
    }
    return 0;
}